<!--自定义表格组件，满足自动填满父容器剩余空间，筛选，排序，编辑表格等复杂操作-->
<!--向父组件可触发的事件有：表格加载完成tableLoadingReady(this)，复选框选中selectedChange(arr)，单击选择行singleClick(row)，
双击编辑单元格doubleClick(currentRow, filedInfo)，分页发生改变sizeChange(pageNum, pageSize)，拖拽行rowDrag()，导出之前额外处理pretreatment(titleList, tableData, callback)
，行编辑完成单元格值修改完成change(row, field)，表格编辑输入值input(fieldName, row) -->
<template>
  <div style="position: relative;overflow: hidden;" :class="{noSelect:noSelect}">
    <ant-spin ref="cspin">
      <!-- 双击触发表格编辑组件 -->
      <AntTableEditor ref="tableEditor" :editor="editor" :tableThis="tableThis" @editItemBlur="editItemBlur" @tdChange="tdChange"/>
      <!-- 表格工具设置组件 -->
      <AntTableSetting :tableThis="tableThis" :tableShowSetting="tableShowSetting" :excelDownLoad="excelDownLoad" :excelDownLoadSelected="excelDownLoadSelected" :advancedFilter="advancedFilter"/>
      <!-- 表格标题区域 -->
      <div v-show="!hideTableTitle">
        <!-- 左悬浮表格标题 -->
        <table
          width="1px"
          class="c-table"
          ref="leftTableHead"
          style="position: absolute;z-index:1;"
          :style="{'border-top':getBorderStyle(true),'border-right':getBorderStyle(isTableAttrOn('borderCell')),'border-bottom':getBorderStyle(false),'border-left':getBorderStyle(true)}"
          @click="tableThClick($event,'left')"
        >
          <!--colgroup用于对 HTML 表格的列进行逻辑分组，允许通过 CSS 统一设置一组列的样式（如宽度、背景色、边框等），而无需为每个单元格单独定义样式-->
          <colgroup>
            <!--定义移动行，选择行，折叠行，排序行宽度-->
            <col v-if="tableMoveable" :width="defaultTDWidth.moveableTDWidth" />
            <col v-if="selectable" :width="defaultTDWidth.selectableTDWidth" />
            <col v-if="expandable&&expandCustom" :width="defaultTDWidth.expandableTDWidth" />
            <col v-if="tableShowNumber" :width="defaultTDWidth.numberTDWidth" />
            <col v-for="(item,index) of tabTitle.columns.left" :width="item.width+'px'" :key="index" />
          </colgroup>
          <thead :style="titleTheadClass.leftTableHead">
            <tr
              v-for="(trItem,trIndex) of tabTitle.titles.left"
              :style="{'border-bottom':getBorderStyle(true),height:defaultTableTitleHeight+'px'}"
              :key="trIndex"
              @mousemove="thMouseMove"
              @mousedown.stop="thMouseDown"
            >
              <!--cursor="none" 表示 鼠标无样式-->
              <th cursor="none" v-if="trIndex==0 && tableMoveable" :rowspan="tabTitle.titles.left.length"></th>
              <!-- ty:意为表头类型（type），序号no、多选框allCheckbox、thasc正序排序、thdesc倒序排序，这里由于是序号后的第一列，固定写死为多选框，再后面就有可能是被触发为排序的列（正序或倒序） -->
              <th
                v-if="trIndex==0&&selectable"
                cursor="none"
                ty="allCheckbox"
                style="padding:0px;"
                :style="{'border-left':getBorderStyle(true&&tableMoveable)}"
                :rowspan="tabTitle.titles.left.length"
                :class="{'c-check-box':true,'box-selected':allCheckState!='no'}"
                v-html="allCheckFont"
              ></th>
              <th cursor="none" v-if="trIndex==0 && expandable && expandCustom" :rowspan="tabTitle.titles.left.length" :style="{'border-left':getBorderStyle(true&&(tableMoveable||selectable))}"></th>
              <th cursor="none" v-if="trIndex==0 && tableShowNumber" :rowspan="tabTitle.titles.left.length" :style="{'border-left':getBorderStyle(true&&(tableMoveable||selectable||expandable))}"><span>序号</span></th>
              <!-- fi:意为字段索引（fieldIndex），表示当前表头所对应的表格配置字段信息的数组下标，在表格捕获事件之后能够清楚定位到事件是被谁触发，触发事件的对应数据是谁 -->
              <!-- ri:意为行索引（rowIndex），表示当前表头所处多少行的数组索引，也是用于事件定位，因为可能表格是多级列头 -->
              <!-- pos:意为当前表头在整个表格定位的相对位置，分为左、中、右 -->
              <!-- 注意：表格结构分为左中右三大部分，但结构是一样的，之所以没有组件化而是全部重复写出来。一是因为vue组件虽然简化代码，却牺牲了性能，而我们要求较高的性能，故使用重复编写方式；二是为了满足后续可能发生的需求灵活修改。
              表格之所以定义fi、ri之类的属性也是为了提高性能，不使用元素事件，而是将表格内所有发生的事件都冒泡给表格最外层容器。如果为了简化写法，使用元素事件，假如数据足够多的情况下，用户体验几乎会崩溃-->
              <template v-for="(item,index) of trItem">
                <!--表格列的内容为自定义-->
                <th
                  v-if="item.type=='custom'"
                  pos="left"
                  :style="{textAlign:item.align,'border-left':getBorderStyle(isTableAttrOn('borderCell')&&(index>0||(index===0&&(tableMoveable||expandable||selectable||tableShowNumber))))}"
                  :fi="index"
                  :ri="trIndex"
                  :colspan="item.colspan"
                  :rowspan="item.rowspan"
                  :key="index"
                >
                  <span v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.left,item.field,item.titleAttribute.minWidth)">
                    <span v-html="item.titleAttribute.titleHtml"></span>
                  </span>
                  <span v-else :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}">{{ item.title }}</span>
                </th>
                <!--表格列的内容为指定类型-->
                <th
                  v-else
                  ty="sort"
                  pos="left"
                  :style="{textAlign:item.align,'border-left':getBorderStyle(isTableAttrOn('borderCell')&&(index>0||(index===0&&(tableMoveable||expandable||selectable||tableShowNumber))))}"
                  :class="{'c-edit-title':item.edit}"
                  :ri="trIndex"
                  :fi="index"
                  :colspan="item.colspan"
                  :rowspan="item.rowspan"
                  :key="index"
                >
                  <span v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.left,item.field,item.titleAttribute.minWidth)" :class="{'c-table-title-is-not-null':item.notNull}">
                    <span v-html="item.titleAttribute.titleHtml"></span>
                  </span>
                  <!--列必填-->
                  <span v-else :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}">{{ item.title }}</span>
                  <!--列排序-->
                  <div class="c-sort" v-if="item.sort">
                    <span class="c-sort-icon">
                      <a-icon type="caret-up" :style="{height:'5px',color:item.currentSortType==='asc'?'inherit':'#bfbfbf'}"/>
                      <a-icon type="caret-down" :style="{color:item.currentSortType==='desc'?'inherit':'#bfbfbf'}"/>
                    </span>
                  </div>
                  <!--列筛选-->
                  <div class="c-filter" @click="toggleFilterDropdown(item,true)" v-if="item.filter" :class="{'c-filter-active-dropdown':!!item.filterDropdownVisible}">
                    <a-dropdown :visible="!!item.filterDropdownVisible">
                      <a-icon type="filter" :class="{'c-filter-active':filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).size>0}"></a-icon>
                      <div slot="overlay" class="c-filter-dropdown" @mouseleave="toggleFilterDropdown(item,false)">
                        <div class="c-filter-dropdown-div">
                          <a-input-search placeholder="请输入查询内容" @change="(e)=>tableTitleFieldFilterArrSearch(e,item)"></a-input-search>
                        </div>
                        <div class="c-filter-dropdown-div" style="border-bottom:1px solid #E5E6EB;">
                          <a-checkbox
                            :indeterminate="tableTitleFieldFilterCheckedAllStatus(true,item)"
                            :checked="tableTitleFieldFilterCheckedAllStatus(false,item)"
                            @change="(e)=tableTitleFieldFilterCheckedAll(item)">全选</a-checkbox>
                        </div>
                        <div class="c-filter-dropdown-body" :style="{'max-height': ((tableBoundingClientRect.height/2)>200?tableBoundingClientRect.height/2:200)+'px'}" @scroll="(e)=>tableTitleFieldFilterScroll(e,item)">
                          <div class="c-filter-dropdown-checkbox">
                            <div v-for="(el,index) in getTableTitleFieldFilterArrLimit(item)" :key="index" class="c-filter-dropdown-div">
                              <a-checkbox
                                :value="el"
                                :checked="filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).has(el)"
                                @change="(e)=>tableTitleFieldFilterChecked(e,el,item.field)">{{ el }}</a-checkbox>
                            </div>
                          </div>
                        </div>
                      </div>
                    </a-dropdown>
                  </div>
                </th>
              </template>
            </tr>
          </thead>
        </table>
        <!-- 右悬浮表格标题 -->
        <table
          width="1px"
          ref="rightTableHead"
          v-show="tabTitle.columns.right.length>0"
          class="c-table"
          style="position: absolute;z-index:1;border-left:none;"
          :style="{right:'0px','border-top':getBorderStyle(true),'border-right':getBorderStyle(true),'border-bottom':getBorderStyle(false),'border-left':getBorderStyle(isTableAttrOn('borderCell'))}"
          @click="tableThClick($event,'right')"
        >
          <!--colgroup用于对 HTML 表格的列进行逻辑分组，允许通过 CSS 统一设置一组列的样式（如宽度、背景色、边框等），而无需为每个单元格单独定义样式-->
          <colgroup>
            <col
              v-for="(item,index) of tabTitle.columns.right"
              :width="item.width+(item.offsetWidth?item.offsetWidth:0)+(index===(tabTitle.columns.right.length-1)?barWidth:0)+'px'"
              :key="index"
            />
          </colgroup>
          <thead :style="titleTheadClass.rightTableHead">
            <tr
              v-for="(trItem,trIndex) of tabTitle.titles.right"
              :style="{'border-bottom':getBorderStyle(true),height:defaultTableTitleHeight+'px'}"
              :key="trIndex"
              @mousemove="thMouseMove"
              @mousedown.stop="thMouseDown"
            >
              <template v-for="(item,index) of trItem">
                <!--表格列的内容为自定义-->
                <th
                  v-if="item.type=='custom'"
                  ty="sort"
                  pos="right"
                  :style="{textAlign:item.align,'border-left':getBorderStyle(isTableAttrOn('borderCell'))}"
                  :fi="index"
                  :ri="trIndex"
                  :colspan="item.colspan"
                  :rowspan="item.rowspan"
                  :key="index"
                >
                  <span v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.right,item.field,item.titleAttribute.minWidth)">
                    <span v-html="item.titleAttribute.titleHtml"></span>
                  </span>
                  <span v-else :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}">{{ item.title }}</span>
                </th>
                <!--表格列的内容为指定类型-->
                <th
                  v-else
                  ty="sort"
                  pos="right"
                  :style="{textAlign:item.align,'border-left':getBorderStyle(isTableAttrOn('borderCell'))}"
                  :class="{'c-edit-title':item.edit}"
                  :ri="trIndex"
                  :fi="index"
                  :colspan="item.colspan"
                  :rowspan="item.rowspan"
                  :key="index"
                >
                  <span :class="{'c-table-title-is-not-null':item.notNull}" v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.right,item.field,item.titleAttribute.minWidth)">
                    <span :ri="trIndex" :fi="index" ty="sort" :title="item.title" v-html="item.titleAttribute.titleHtml"></span>
                  </span>
                  <!--列必填-->
                  <span v-else ty="sort" :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}" :ri="trIndex" :fi="index" :title="item.title">{{ item.title }}</span>
                  <!--列排序-->
                  <div class="c-sort" v-if="item.sort">
                    <span class="c-sort-icon">
                      <a-icon type="caret-up" :style="{height:'5px',color:item.currentSortType==='asc'?'inherit':'#bfbfbf'}"/>
                      <a-icon type="caret-down" :style="{color:item.currentSortType==='desc'?'inherit':'#bfbfbf'}"/>
                    </span>
                  </div>
                  <!--列筛选-->
                  <div class="c-filter" @click="toggleFilterDropdown(item,true)" v-if="item.filter" :class="{'c-filter-active-dropdown':!!item.filterDropdownVisible}">
                    <a-dropdown :visible="!!item.filterDropdownVisible">
                      <a-icon type="filter" :class="{'c-filter-active':filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).size>0}"></a-icon>
                      <div slot="overlay" class="c-filter-dropdown" @mouseleave="toggleFilterDropdown(item,false)">
                        <div class="c-filter-dropdown-div">
                          <a-input-search placeholder="请输入查询内容" @change="(e)=>tableTitleFieldFilterArrSearch(e,item)"></a-input-search>
                        </div>
                        <div class="c-filter-dropdown-div" style="border-bottom:1px solid #E5E6EB;">
                          <a-checkbox
                            :indeterminate="tableTitleFieldFilterCheckedAllStatus(true,item)"
                            :checked="tableTitleFieldFilterCheckedAllStatus(false,item)"
                            @change="(e)=tableTitleFieldFilterCheckedAll(item)">全选</a-checkbox>
                        </div>
                        <div class="c-filter-dropdown-body" :style="{'max-height': ((tableBoundingClientRect.height/2)>200?tableBoundingClientRect.height/2:200)+'px'}" @scroll="(e)=>tableTitleFieldFilterScroll(e,item)">
                          <div class="c-filter-dropdown-checkbox">
                            <div v-for="(el,index) in getTableTitleFieldFilterArrLimit(item)" :key="index" class="c-filter-dropdown-div">
                              <a-checkbox
                                :value="el"
                                :checked="filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).has(el)"
                                @change="(e)=>tableTitleFieldFilterChecked(e,el,item.field)">{{ el }}</a-checkbox>
                            </div>
                          </div>
                        </div>
                      </div>
                    </a-dropdown>
                  </div>
                </th>
              </template>
            </tr>
          </thead>
        </table>
        <!-- 中间表格标题 -->
        <div
          ref="centerTableHead"
          class="c-title-table-body"
          :style="{'border-right-width':scrollBarWidthY+'px'}"
        >
          <table
            width="1px"
            class="c-table"
            :style="{marginLeft:tableFixedMargin.left,'border-top':getBorderStyle(true),'border-right':getBorderStyle(true),'border-bottom':getBorderStyle(false),'border-left':getBorderStyle(false)}"
            @click="tableThClick"
          >
            <!--colgroup用于对 HTML 表格的列进行逻辑分组，允许通过 CSS 统一设置一组列的样式（如宽度、背景色、边框等），而无需为每个单元格单独定义样式-->
            <colgroup>
              <col
                align="right"
                v-for="(item,index) of tabTitle.columns.center"
                :width="item.width+'px'"
                :key="index"
              />
              <col :width="tableFixedMargin.right" />
            </colgroup>
            <thead :style="titleTheadClass.centerTableHead">
              <tr
                v-for="(trItem,trIndex) of tabTitle.titles.center"
                :key="trIndex"
                :style="{'border-bottom':getBorderStyle(true),height:defaultTableTitleHeight+'px'}"
                @mousemove="thMouseMove"
                @mousedown.stop="thMouseDown"
              >
                <template v-for="(item,index) of trItem">
                  <!--表格列的内容为自定义-->
                  <th
                    v-if="item.type=='custom'"
                    ty="sort"
                    pos="center"
                    :style="{textAlign:item.align,'border-left':trIndex===0&&tableFixedMargin.leftNum>0&&index===0?null:getBorderStyle(isTableAttrOn('borderCell')),'border-right':(trIndex===tabTitle.titles.center.length-1)&&tabTitle.columns.right.length===0?getBorderStyle(true):null}"
                    :fi="index"
                    :ri="trIndex"
                    :colspan="item.colspan"
                    :rowspan="item.rowspan"
                    :key="index"
                  >
                    <span
                      v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.center,item.field,item.titleAttribute.minWidth)"
                    >
                      <span
                        ty="sort"
                        :ri="trIndex"
                        :fi="index"
                        :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}"
                        :title="item.title"
                        v-html="item.titleAttribute.titleHtml"
                      ></span>
                    </span>
                    <span
                      :ri="trIndex"
                      :fi="index"
                      ty="sort"
                      :title="item.title"
                      v-else
                    >{{ item.title }}</span>
                  </th>
                  <!--表格列的内容为指定类型-->
                  <th
                    v-else
                    ty="sort"
                    pos="center"
                    :style="{textAlign:item.align,'border-left':trIndex===0&&tableFixedMargin.leftNum>0&&index===0?null:getBorderStyle(isTableAttrOn('borderCell')),'border-right':(trIndex===tabTitle.titles.center.length-1)&&tabTitle.columns.right.length===0?getBorderStyle(true):null}"
                    :class="{'c-edit-title':item.edit}"
                    :ri="trIndex"
                    :fi="index"
                    :colspan="item.colspan"
                    :rowspan="item.rowspan"
                    :key="index"
                  >
                    <span v-if="item.titleAttribute&&titleCombineByWidth(tabTitle.columns.center,item.field,item.titleAttribute.minWidth)" :class="{'c-table-title-is-not-null':item.notNull}">
                      <span :ri="trIndex" :fi="index" ty="sort" :title="item.title" v-html="item.titleAttribute.titleHtml"></span>
                    </span>
                    <!--列必填-->
                    <span v-else ty="sort" :class="{'c-table-title-is-not-null':item.notNull,'c-edit-title':item.edit}" :ri="trIndex" :fi="index" :title="item.title">{{ item.title }}</span>
                    <!--列排序-->
                    <div class="c-sort" v-if="item.sort">
                      <span class="c-sort-icon">
                        <a-icon type="caret-up" :style="{height:'5px',color:item.currentSortType==='asc'?'inherit':'#bfbfbf'}"/>
                        <a-icon type="caret-down" :style="{color:item.currentSortType==='desc'?'inherit':'#bfbfbf'}"/>
                      </span>
                    </div>
                    <!--列筛选-->
                    <div class="c-filter" @click="toggleFilterDropdown(item,true)" v-if="item.filter" :class="{'c-filter-active-dropdown':!!item.filterDropdownVisible}">
                      <a-dropdown :visible="!!item.filterDropdownVisible">
                        <a-icon type="filter" :class="{'c-filter-active':filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).size>0}"></a-icon>
                        <div slot="overlay" class="c-filter-dropdown" @mouseleave="toggleFilterDropdown(item,false)">
                          <div class="c-filter-dropdown-div">
                            <a-input-search placeholder="请输入查询内容" @change="(e)=>tableTitleFieldFilterArrSearch(e,item)"></a-input-search>
                          </div>
                          <div class="c-filter-dropdown-div" style="border-bottom:1px solid #E5E6EB;">
                            <a-checkbox
                              :indeterminate="tableTitleFieldFilterCheckedAllStatus(true,item)"
                              :checked="tableTitleFieldFilterCheckedAllStatus(false,item)"
                              @change="(e)=tableTitleFieldFilterCheckedAll(item)">全选</a-checkbox>
                          </div>
                          <div class="c-filter-dropdown-body" :style="{'max-height': ((tableBoundingClientRect.height/2)>200?tableBoundingClientRect.height/2:200)+'px'}" @scroll="(e)=>tableTitleFieldFilterScroll(e,item)">
                            <div class="c-filter-dropdown-checkbox">
                              <div v-for="(el,index) in getTableTitleFieldFilterArrLimit(item)" :key="index" class="c-filter-dropdown-div">
                                <a-checkbox
                                  :value="el"
                                  :checked="filterFieldMap.get(item.field)&&filterFieldMap.get(item.field).has(el)"
                                  @change="(e)=>tableTitleFieldFilterChecked(e,el,item.field)">{{ el }}</a-checkbox>
                              </div>
                            </div>
                          </div>
                        </div>
                      </a-dropdown>
                    </div>
                  </th>
                </template>
              </tr>
            </thead>
          </table>
        </div>
      </div>
      <!-- 表格内容区域 -->
      <div v-show="tableData.length>0" ref="contentTable" :style="{'position': 'relative'}">
        <!-- 左悬浮表格数据 -->
        <div
          ref="leftTableBody"
          class="c-fiexd"
          :style="{height:(tableBodyHeight-scrollBarWidthX)+'px'}"
          @scroll="tableScroll"
        >
          <table
            width="1"
            :style="{'border-top':false,'border-right':getBorderStyle(isTableAttrOn('borderCell')),'border-bottom':getBorderStyle(true),'border-left':getBorderStyle(true)}"
            :class="{'c-table':true,'td-nowrap':!isTableAttrOn('supportLineBreak')}"
            @click="singleClick($event,'left')"
            @dblclick.stop="doubleClick($event,'left')"
          >
            <colgroup>
              <col v-if="tableMoveable" :width="defaultTDWidth.moveableTDWidth" />
              <col v-if="selectable" :width="defaultTDWidth.selectableTDWidth" />
              <col v-if="expandable&&expandCustom" :width="defaultTDWidth.expandableTDWidth" />
              <col v-if="tableShowNumber" :width="defaultTDWidth.numberTDWidth" />
              <col v-for="(item,index) of tabTitle.columns.left" :width="item.width+'px'" :key="index" />
            </colgroup>
            <tbody>
              <tr
                ref="ctr"
                v-for="(dataItem,index1) of loadAllTableData"
                :class="{'c-table-current-row':dataItem===currentRow,'remove-row-transition':!!dataItem._c_meta_.translateY}"
                :style="{height:dataItem._c_meta_&&dataItem._c_meta_.height?(dataItem._c_meta_.height+'px'):'auto','background':changeRowColor(index1),'transform':'translateY('+(!!dataItem._c_meta_.translateY?dataItem._c_meta_.translateY:0)+'px)','border-bottom':getBorderStyle((index1+1)!==loadAllTableData.length)}"
                :key="index1"
                :rowIndex="index1"
                :id="dataItem._c_meta_.key"
              >
                <!--拖拽排序-->
                <td v-if="tableMoveable" :ri="index1" rty="dragAndSort" @mousedown="(e)=>nodeOnmousedown(e)" :style="{cursor:tableMoveable&&!(expandable && dataItem._c_meta_.parentData)?'move':null,'text-align':'center'}">
                  <svg
                    v-if="!(expandable && dataItem._c_meta_.parentData)"
                    viewBox="64 64 896 896"
                    focusable="false"
                    data-icon="holder"
                    width="1em"
                    height="1em"
                    fill="currentColor"
                    aria-hidden="true">
                    <path d="M300 276.5a56 56 0 1056-97 56 56 0 00-56 97zm0 284a56 56 0 1056-97 56 56 0 00-56 97zM640 228a56 56 0 10112 0 56 56 0 00-112 0zm0 284a56 56 0 10112 0 56 56 0 00-112 0zM300 844.5a56 56 0 1056-97 56 56 0 00-56 97zM640 796a56 56 0 10112 0 56 56 0 00-112 0z"></path>
                  </svg>
                </td>
                <!--多选-->
                <td
                  v-if="selectable&&!(expandable && expandCustom && dataItem._c_meta_.parentData)&&!viewMode"
                  :ri="index1"
                  style="padding:0px;"
                  rty="checkbox"
                  :class="{'c-check-box':true,'box-selected':dataItem.cChecked}"
                  :style="{'border-left':getBorderStyle(true&&tableMoveable)}"
                >{{ dataItem.cChecked?'&#xe6cb;':'&#xe637;' }}
                </td>
                <!--折叠-->
                <td v-if="expandable&&expandCustom" :ri="index1" rty="rowExpansion" :style="{'border-left':getBorderStyle(true&&(tableMoveable||selectable)),'text-align':'center'}">
                  <div style="display: flex;justify-content: center;width: 100%;">
                    <div
                      v-show="dataItem._c_meta_&&dataItem._c_meta_.expandIcon"
                      :class="{'c-table-row-expand-icon':true,'c-table-row-expand-icon-collapsed':!dataItem._c_meta_.expandHide}"
                      @click.stop="(e)=>{rowExpansion(e,dataItem,!dataItem._c_meta_.expandHide)}"></div>
                  </div>
                </td>
                <!--序号-->
                <td
                  v-if="tableShowNumber"
                  class="c-default-font"
                  :ri="index1"
                  rty="no"
                  :style="{'border-left':getBorderStyle(true&&(tableMoveable||selectable||expandable))}"
                >
                  {{ dataItem._c_meta_.number }}
                </td>
                <template v-for="(fieldItem,index2) in tabTitle.columns.left">
                  <!--自定义数据-->
                  <td
                    v-if="fieldItem.type=='custom'"
                    class="c-default-font"
                    :ri="index1"
                    :fi="index2"
                    rty="custom"
                    :key="index2"
                    :style="{'border-left':getBorderStyle(isTableAttrOn('borderCell')&&(index2>0||(index2===0&&(tableMoveable||expandable||selectable||tableShowNumber))))}"
                  >
                    <slot
                      :name="fieldItem.name"
                      :row="dataItem"
                      :row-index="index1"
                      :column-index="index2"
                      :no-index="(pageNum-1)*pageSize+index1"
                    ></slot>
                  </td>
                  <!--指定类型数据-->
                  <td
                    v-else
                    class="c-default-font"
                    :ri="index1"
                    :fi="index2"
                    :pf="fieldItem.field"
                    :style="{...getStyle(fieldItem,dataItem),'border-left':getBorderStyle(isTableAttrOn('borderCell')&&(index2>0||(index2===0&&(tableMoveable||expandable||selectable||tableShowNumber))))}"
                    :class="{'c-font-box':isAliFont(fieldItem),'switch-selected':isSelected(fieldItem,dataItem)}"
                    :title="formatTdTitle(fieldItem,dataItem)"
                    :key="index2"
                  >
                    <!--折叠子数据-->
                    <template v-if="expandable && !expandCustom && fieldItem.field === getExpandTreeFiled.field">
                      <span class="c-table-row-indent" :ri="index1" :fi="index2" :pf="fieldItem.field" :style="{'padding-left' : dataItem._c_meta_.paddingLeft + 'px'}"></span>
                      <div
                        v-if="dataItem.children"
                        :class="{'c-table-row-expand-icon':true,'c-table-row-expand-icon-collapsed':!dataItem._c_meta_.expandHide}"
                        @click.stop="(e)=>{rowExpansion(e,dataItem,!dataItem._c_meta_.expandHide)}"
                      ></div>
                    </template>
                    <span :ri="index1" :fi="index2" :pf="fieldItem.field" v-html="formatTd(fieldItem,dataItem)"></span>
                    <div
                      v-if="isEdited(fieldItem,dataItem)&&dataItem._c_meta_['edit_icon_'+fieldItem.field]"
                      class="c-table-td-edited-box"
                      :style="{left:(dataItem._c_meta_['edit_icon_'+fieldItem.field].left+fieldItem.width)+'px',top:dataItem._c_meta_['edit_icon_'+fieldItem.field].top+'px'}"
                    >
                      <a-icon type="check" class="c-table-td-edited-box-icon"/>
                    </div>
                  </td>
                </template>
              </tr>
            </tbody>
          </table>
        </div>
        <!-- 右悬浮表格 -->
        <div
          ref="rightTableBody"
          class="c-fiexd"
          v-show="tabTitle.columns.right.length>0"
          :style="{height:(tableBodyHeight-scrollBarWidthX)+'px',right:scrollBarWidthY+'px'}"
          @scroll="tableScroll"
        >
          <table
            width="1"
            :style="{'border-top':false,'border-right':getBorderStyle(true),'border-bottom':getBorderStyle(true),'border-left':getBorderStyle(isTableAttrOn('borderCell'))}"
            :class="{'c-table':true,'td-nowrap':!isTableAttrOn('supportLineBreak')}"
            @click="singleClick($event,'right')"
            @dblclick.stop="doubleClick($event,'right')"
          >
            <colgroup>
              <col
                v-for="(item,index) of tabTitle.columns.right"
                :width="item.width+(item.offsetWidth?item.offsetWidth:0)+((index===(tabTitle.columns.right.length-1)&&!tableScrollVisible.y)?barWidth:0)+'px'"
                :key="index"
              />
            </colgroup>
            <tbody>
              <tr
                ref="ctr"
                v-for="(dataItem,index1) of loadAllTableData"
                :class="{'c-table-current-row':dataItem===currentRow,'remove-row-transition':!!dataItem._c_meta_.translateY}"
                :style="{height:dataItem._c_meta_&&dataItem._c_meta_.height?(dataItem._c_meta_.height+'px'):'auto','background':changeRowColor(index1),'transform':'translateY('+(!!dataItem._c_meta_.translateY?dataItem._c_meta_.translateY:0)+'px)','border-bottom':getBorderStyle((index1+1)!==loadAllTableData.length)}"
                :key="index1"
                :rowIndex="index1"
              >
                <template v-for="(fieldItem,index2) in tabTitle.columns.right">
                  <!--自定义数据-->
                  <td
                    v-if="fieldItem.type=='custom'"
                    class="c-default-font"
                    :ri="index1"
                    :fi="index2"
                    rty="custom"
                    :key="index2"
                    :style="{'border-left':getBorderStyle(index2>0&&isTableAttrOn('borderCell'))}"
                  >
                    <slot
                      :name="fieldItem.name"
                      :row="dataItem"
                      :row-index="index1"
                      :column-index="index2"
                      :no-index="(pageNum-1)*pageSize+index1"
                    ></slot>
                  </td>
                  <!--指定类型数据-->
                  <td
                    v-else
                    class="c-default-font"
                    :ri="index1"
                    :fi="index2"
                    :pf="fieldItem.field"
                    :style="{...getStyle(fieldItem,dataItem),'border-left':getBorderStyle(index2>0&isTableAttrOn('borderCell'))}"
                    :class="{'c-font-box':isAliFont(fieldItem),'switch-selected':isSelected(fieldItem,dataItem)}"
                    :title="formatTdTitle(fieldItem,dataItem)"
                    :key="index2"
                  >
                    <!--折叠子数据-->
                    <template v-if="expandable && !expandCustom && fieldItem.field === getExpandTreeFiled.field">
                      <span class="c-table-row-indent" :ri="index1" :fi="index2" :pf="fieldItem.field" :style="{'padding-left' : dataItem._c_meta_.paddingLeft + 'px'}"></span>
                      <div
                        v-if="dataItem.children"
                        :class="{'c-table-row-expand-icon':true,'c-table-row-expand-icon-collapsed':!dataItem._c_meta_.expandHide}"
                        @click.stop="(e)=>{rowExpansion(e,dataItem,!dataItem._c_meta_.expandHide)}"
                      ></div>
                    </template>
                    <span :ri="index1" :fi="index2" :pf="fieldItem.field" v-html="formatTd(fieldItem,dataItem)"></span>
                    <div
                      v-if="isEdited(fieldItem,dataItem)&&dataItem._c_meta_['edit_icon_'+fieldItem.field]"
                      class="c-table-td-edited-box"
                      :style="{left:(dataItem._c_meta_['edit_icon_'+fieldItem.field].left+fieldItem.width)+'px',top:dataItem._c_meta_['edit_icon_'+fieldItem.field].top+'px'}"
                    >
                      <a-icon type="check" class="c-table-td-edited-box-icon"/>
                    </div>
                  </td>
                </template>
              </tr>
            </tbody>
          </table>
        </div>
        <!-- 中间表格 -->
        <div
          ref="centerTableBody"
          class="c-table-body"
          :style="{height:tableBodyHeight+'px','overflow-x':tableScrollVisible.x?'scroll':'hidden','overflow-y':tableScrollVisible.y?'scroll':'hidden'}"
          @scroll="tableScroll($event,'center')"
        >
          <table
            ref="centerTableBodyMain"
            width="1px"
            :style="{marginLeft:tableFixedMargin.left,'border-top':false,'border-right':getBorderStyle(true),'border-bottom':getBorderStyle(true),'border-left':false}"
            :class="{'c-table':true,'td-nowrap':!isTableAttrOn('supportLineBreak'),'border-top':false,'border-right':true,'border-bottom':true,'border-left':false}"
            @click.stop="singleClick"
            @dblclick.stop="doubleClick"
          >
            <colgroup>
              <col
                align="right"
                v-for="(item,index) of tabTitle.columns.center"
                :width="item.width"
                :key="index"
              />
              <col :width="tableFixedMargin.right" />
            </colgroup>
            <tbody ref="tbodyCenter">
              <tr
                ref="ctr"
                v-for="(dataItem,index1) of loadAllTableData"
                :class="{'c-table-current-row':dataItem===currentRow,'remove-row-transition':!!dataItem._c_meta_.translateY}"
                :style="{height:dataItem._c_meta_&&dataItem._c_meta_.height?(dataItem._c_meta_.height+'px'):'auto','background':changeRowColor(index1),'transform':'translateY('+(!!dataItem._c_meta_.translateY?dataItem._c_meta_.translateY:0)+'px)','border-bottom':getBorderStyle((index1+1)!==loadAllTableData.length)}"
                :key="index1"
                :rowIndex="index1"
              >
                <template v-for="(fieldItem,index2) in tabTitle.columns.center">
                  <!--自定义数据-->
                  <td class="c-default-font" v-if="fieldItem.type=='custom'" rty="custom" :key="index2" :style="{'border-left':tableFixedMargin.leftNum>0&&index2===0?null:getBorderStyle(isTableAttrOn('borderCell'))}">
                    <slot
                      :name="fieldItem.name"
                      :row="dataItem"
                      :row-index="index1"
                      :column-index="index2"
                      :no-index="(pageNum-1)*pageSize+index1"
                    ></slot>
                  </td>
                  <!--指定类型数据-->
                  <td
                    v-else
                    class="c-default-font"
                    :ri="index1"
                    :fi="index2"
                    :pf="fieldItem.field"
                    :style="{...getStyle(fieldItem,dataItem),'border-left':tableFixedMargin.leftNum>0&&index2===0?null:getBorderStyle(isTableAttrOn('borderCell'))}"
                    :class="{'c-font-box':isAliFont(fieldItem),'switch-selected':isSelected(fieldItem,dataItem)}"
                    :title="formatTdTitle(fieldItem,dataItem)"
                    :key="index2"
                  >
                    <!--折叠子数据-->
                    <template v-if="expandable && !expandCustom && fieldItem.field === getExpandTreeFiled.field">
                      <span class="c-table-row-indent" :ri="index1" :fi="index2" :pf="fieldItem.field" :style="{'padding-left' : dataItem._c_meta_.paddingLeft + 'px'}"></span>
                      <div
                        v-if="dataItem.children"
                        :class="{'c-table-row-expand-icon':true,'c-table-row-expand-icon-collapsed':!dataItem._c_meta_.expandHide}"
                        @click.stop="(e)=>{rowExpansion(e,dataItem,!dataItem._c_meta_.expandHide)}"
                      ></div>
                    </template>
                    <span :ri="index1" :fi="index2" :pf="fieldItem.field" v-html="formatTd(fieldItem,dataItem)"></span>
                    <div
                      v-if="isEdited(fieldItem,dataItem)&&dataItem._c_meta_['edit_icon_'+fieldItem.field]"
                      class="c-table-td-edited-box"
                      :style="{left:(dataItem._c_meta_['edit_icon_'+fieldItem.field].left+fieldItem.width)+'px',top:dataItem._c_meta_['edit_icon_'+fieldItem.field].top+'px'}"
                    >
                      <a-icon type="check" class="c-table-td-edited-box-icon"/>
                    </div>
                  </td>
                </template>
                <td></td>
              </tr>
            </tbody>
          </table>
          <!-- 展开行 - 自定义模式 div遮罩 -->
          <div v-if="expandCustom">
            <div
              v-for="item in loadAllTableData.filter(el=>el._c_meta_.parentData)"
              :ref="'expand_row_'+item._c_meta_.key"
              :key="item._c_meta_.key"
              :style="{width:tableBoundingClientRect.width+'px',height:item._c_meta_.expandRowHeight+'px',
                       top:item._c_meta_.top+'px',left:item._c_meta_.left+'px','transform':'translateY('+(!!item._c_meta_.translateY?item._c_meta_.translateY:0)+'px)',
                       'border-top':getBorderStyle(true),'border-right':getBorderStyle(true),'border-bottom':getBorderStyle(true),'border-left':getBorderStyle(true)}"
              :class="{'c-expand-row':true,'remove-row-transition':!!item._c_meta_.translateY}"
            >
              <slot name="rowExpansion" :row="item"></slot>
            </div>
          </div>
        </div>
        <!-- 行移动辅助线 -->
        <div
          class="row-move-line"
          :style="{display:removeLine.display,top:removeLine.top+'px',left:removeLine.left+'px'}"
        ></div>
        <!-- 行移动提示 -->
        <div
          v-if="currentRow&&tableMoveInfoFields"
          class="row-move-line-info"
          :style="{display:removeLine.info.display,top:removeLine.info.top+'px',left:removeLine.info.left+'px'}"
        >
          <div class="row-move-line-info-row">
            <div
              v-for="(item,index) in parseTableMoveInfo"
              :key="index"
              :style="{'margin-left':index>0?'5px':null}"
            >{{ formatTdTitle(item,currentRow) }}</div>
          </div>
        </div>
      </div>
      <div v-show="tableData.length===0" class="c-default-font c-no-data">
        暂无数据
      </div>
      <!-- 分页组件 -->
      <a-pagination
        v-if="pageable"
        :show-quick-jumper="size==='large'"
        show-size-changer
        :total="total"
        :show-total="total => `共 ${total} 条`"
        @change="pageChange"
        :page-size.sync="pageSize"
        v-model="pageNum"
        style="text-align: right;margin: 3px 0;white-space:nowrap;"
        @showSizeChange="sizeChange"
        :page-size-options="['20','50','100','200']"
        :default-page-size="20"
      />
    </ant-spin>
  </div>
</template>

<script>
import './css/table.less'
import { d } from './js/table'
import { preHandler } from './js/page-table-pre.js'
import AntTableEditor from './components/AntTableEditor.vue'
import AntTableSetting from './components/AntTableSetting.vue'
export default {
  name: 'AntTable',
  components: {
    AntTableEditor,
    AntTableSetting,
  },
  props: {
    /**
     * 表格传入的列配置信息
     */
    tableTitle:{
      type: Array,
      default: () => [
          //数据ID，隐藏显示
          {
            //表格标题
            title: 'ID',
            //列宽，不给会自适应大小
            width: 100,
            //对应数据的字段
            field: 'id',
            hidden: true
          },
          //普通文本显示
          {
            title: '姓名',
            width: 100,
            field: 'userName'
          },
          //日期显示
          {
            title: '开始日期',
            field: 'startDate',
            width: 100,
            type: 'date',
            format: 'YYYY-MM-DD'
          },
          //合并单元格
          {
            title: '合并单元格',
            align: 'center',
            children: [
              {
                title: '单元格1',
                field: 'cell1',
                width: 100,
                //不为空列
                notNull: true,
                //可编辑列
                edit: true
              },
              {
                title: '单元格2',
                field: 'cell2',
                width: 100,
                edit: true
              },
            ]
          },
          //下拉树显示,传入接口地址
          {
            title: '使用部门',
            field: 'deptId',
            width: 120,
            //是否多选
            multi:true,
            //单元格类型，可选项 text（文本），number（数值，小数），selector（下拉），tree（下拉树），date（日期），checkBox（勾选框），custom（自定义），click，switch，dynamic
            type: 'tree',
            url: '/system/user/deptTree',
          },
          //下拉树显示2，指定显示字段和值字段，传入固定值
          {
            title: '使用部门2',
            field: 'deptId2',
            width: 120,
            type: 'tree',
            //指定显示的字段
            dataTitle: 'text',
            //指定为值的字段
            dataValue: 'value',
            dropdownList: [{label: '类别1', id: '0',children:[{label: '类别1-1', id: '2'}]}, {label: '类别2', id: '1'}]
          },
          //下拉显示,传入接口地址
          {
            title: '通知类型',
            field: 'type',
            type: 'selector',
            url:"/system/dict/data/type/sys_notice_type"
          },
          //下拉显示，指定显示字段和值字段，传入固定值
          {
            title: '种类',
            field: 'series',
            type: 'selector',
            //是否多选
            multi:true,
            //指定显示的字段
            dataTitle: 'text',
            //指定为值的字段
            dataValue: 'value',
            dropdownList: [{text: '类别1', value: '0'}, {text: '类别2', value: '1'}],
          },
          {
            title: '判断',
            field: 'judge',
            width: 50,
            type: 'selector',
            dataTitle: 'text',
            dataValue: 'value',
            dropdownList: [{ text: '不合格', value: '0' }, { text: '合格', value: '1' }, { text: '不符合', value: '2' }, { text: '符合', value: '3' }],
            //如果每行的下拉数据不同，通过customDropdownList传入函数
            customDropdownList: (row) => {
              if (row.type == '0') {
                return [{ text: '不合格', value: '0' }, { text: '合格', value: '1' }]
              }else{
                return [{ text: '不符合', value: '2' }, { text: '符合', value: '3' }]
              }
            },
            //自定义每个单元格样式
            style: function (data, row) {
              if (row.judge == '0') {
                return {'background-color': 'red'}
              }
            },
            edit: false
          },
          //数值类型，自定义显示，设置小数点位数
          {
            title: '金额',
            field: 'money',
            width: 80,
            type: 'number',
            decimal: 5,
            edit: true,
            formater: function (data, row) {
              const number = parseFloat(row.inspResult)
              return isNaN(number) ? '' : number
            }
          },
          //勾选框
          {
            title: '启用标志',
            type: 'checkBox',
            field: 'flag',
            width: 80,
            off: '0',
            on: '1',
            hidden: true
          },
          //操作列， 在使用的父组件中，在该组件标签中插入自定义模板内容，info.row可以拿到当前行数据，根据name的值对应，例如
          /*         <template v-slot:action="info">
                        <AntTableButtonGroup>
                          <a @click="mainDetail(info.row)">详情</a>
                          <a-popconfirm title="确定删除吗?" @confirm="() => mainDelete(info.row)"><a>删除</a></a-popconfirm>
                        </AntTableButtonGroup>
                     </template> */
          {
            title: '操作',
            name: 'action',
            type: 'custom',
            width: 135,
            fixed: 'right'
          }
      ]
    },
    /**
     * 如果需要表格为固定高度，从这个值传入，默认自动计算为组件父容器的大小
     */
    tableHeight: { type: Number, default: 250 },
    /**
     *  是否自动计算表格在浏览器中初始化的可视区域高度，若不开启，则跟随父级高度或自定义高度；开启后自动计算，优先级高于tableHeight
     */
    autoCalcVisible: { type: Boolean, default: true },
    /**
     * 表格分页大小
     */
    tablePageSize: { type: Number, default: 20 },
    /**
     * 数据集的主键字段，配置主键字段会影响分页多选是否有效，必须在tableTitle中存在字段
     */
    rowKey: {},
    /**
     * 是否多选
     */
    selectable: { type: Boolean, default: false },
    /**
     * 是否分页
     */
    pageable: { type: Boolean, default: true },
    /**
     * 是否加载该url数据给表格，使用这种方式要求url返回的数据结构按照该组件的标准，否则无法渲染，例如{"total":0,"rows":[{}]}
     */
    url: {},
    /**
     * 自定义请求参数
     */
    param: {},
    /**
     * 是否自动加载，前提是配置url。填false的话配置url也不会自动加载
     */
    autoLoad: { type: Boolean, default: true },
    /**
     * 取到数据之后，但在渲染之前回调的方法，可在这里拦截并做修改
     */
    loadBefore: {},
    /**
     * 是否显示表格设置工具栏
     */
    tableShowSetting: { type: Boolean, default: true },
    /**
     * 是否显示序号
     */
    tableShowNumber: { type: Boolean, default: true },
    /**
     * 是否不同行显示不同颜色
     */
    tableRowChangeColor: { type: Boolean, default: false },
    /**
     * 是否禁用表格排序，禁用后所有列中sort为true的列都无法排序
     */
    tableSort: { type: Boolean, default: true },
    /**
     * 是否开启拖拽移动行
     */
    tableMoveable: { type: Boolean, default: false },
    /**
     * 拖拽移动行提示信息（根据field自动筛选，多个请用,号隔离）
     */
    tableMoveInfoFields: { type: String, default: null },
    /**
     * excel对象，请传入excel文件名和sheetName,如果需要在导出之前对数据进行额外处理，传入pretreatment
     */
    excelInfo: {
      type: Object,
      default: () => ({
        fileName:"导出数据",
        sheetName:"数据",
        pretreatment:null
      })
    },
    /**
     * 导出excel的接口地址
     */
    excelUrl: { type: String, default: "/file/exportTableData" },
    /**
     * 是否隐藏标题行
     */
    hideTableTitle: { type: Boolean, default: false },
    /**
     * 是否折叠行，对于树形数据，当数据中含有children[...]子数据时可启用
     */
    expandable: { type: Boolean, default: false },
    /**
     * 行展开内容自定义 - 默认不开启（结合expandable使用）
     */
    expandCustom: { type: Boolean, default: false },
    /**
     * 行展开自定义高度，expandCostom启用时生效 - 默认 200
     */
    expandRowHeight: { type: Number, default: 200 },
    /**
     * 表格大小，默认最大
     */
    size: { type: String, default: 'large' },
    /**
     * 表格数据加载时是否显示加载中loading，默认false
     */
    loadSpinning: { type: Boolean, default: false },
    /**
     * 视图模式 - 不可编辑操作，默认可操作
     */
    viewMode: { type: Boolean, default: false },
    /**
     * 查询所有数据导出Excel 默认不开启
     */
    excelDownLoad: { type: Boolean, default: false },
    /**
     * 选择数据导出Excel 默认不开启
     */
    excelDownLoadSelected: { type: Boolean, default: false },
    /**
     * 高级筛选 默认开启 前期测试-正式上线后需调整
     */
    advancedFilter: { type: Boolean, default: false }
  },
  data() {
    return {
      //单元格默认宽度
      defaultTDWidth: {
        moveableTDWidth: 46, // 行拖动单元格默认宽度
        expandableTDWidth: 46, // 行展开单元格默认宽度
        selectableTDWidth: 46, // 复选框单元格默认宽度
        numberTDWidth: 46, // 排序单元格默认宽度
        otherTDWidth: 150// 其他单元格默认宽度
      },
      // 默认树形展开右侧距离
      defaultExpandTreeMarginRight: 28,
      // 默认分页器高度
      defaultPageHeight: 32,
      // 默认表格标题高度
      defaultTableTitleHeight: 40,
      // 默认编辑成功提示框大小
      defaultEditorIconWidth: 15,
      // 表格自动计算的高度，这里的400是默认值，组件渲染会第一时间自动计算并替换掉这个值
      tableAutoHeight: 400,
      // 滚动条的宽度，由于整体css被修改过滚动条样式，8是默认值，但IE下宽度不能被修改，组件渲染会第一时间计算并替换这个值
      barWidth: 8,
      // 表格滚动的Y轴距离，是以这个值来控制表格滚动，因为有几块表格同时联动
      scrollTop: 0,
      // 表格滚动的X轴距离，是以这个值来控制表格滚动，因为有几块表格同时联动
      scrollLeft: 0,
      // 表格容器DOM尺寸对象，用于表格各部分使用这个对象进行计算大小
      tableBoundingClientRect: 0,
      // 列头多选框头部的多选状态标识，no-一个都没选，half-选了部分，all-全选
      allCheckState: 'no',
      // 表格标题样式
      titleTheadClass: {},
      // 存放被预处理过的字段配置信息，也是表格最终使用的配置信息，如配置url自动加载字典，如下拉框类型自动渲染formater函数。在配置中没有但功能有的都会被默认处理并放在这个变量中
      preTableTitle: [],
      // 当前分页参数对象，这个对象是外部调用方法时可能结合其他扩展参数
      currentParam: {
        pageNum: 1,
        pageSize: this.tablePageSize
      },
      // 表格数据总条数，由服务端返回查询的集合总数，与当前页面渲染条数无关
      total: 0,
      // 当前页
      pageNum: 1,
      // 分页大小
      pageSize: this.tablePageSize,
      // 表格组件当前界面渲染可见的数据集合 - 从后台获取到的数据
      tableData: [],
      // 行展开存放的子数据
      expansionData: [],
      // 表格当前选中的行，表格中表现为选中行背景为灰色
      currentRow: undefined,
      // 存放多选的行集合
      selectedMap: new Map(),
      // 存放被删除的行集合 复选框
      selectedDeleteMap: new Map(),
      // 选中数据key值缓存区 - 用于外部设置已选中数据，但selectedMap中暂时不存在的数据
      selectedKeysCacheSet: new Set(),
      // 字典缓存，key为缓存的字典名称，value为缓存的字典map。为了避免重复请求字典数据
      dicCacheMap: new Map(),
      //编辑单元格的信息，当type改变时，对应组件会显示出来
      editor: {
        // 行内编辑状态单例变量，如行内点击了下拉框，这里的type是'selector',row是点击做在行的数据
        type: undefined, // 行内编辑浮现的组件类型
        value: undefined, // 行内编辑框内正在编辑的值，下拉框为选中的值
        row: undefined, // 行内编辑所处的行对应的数据
        fieldName: undefined, // 行内编辑对应行的对应字段名称
        top: '0px', // 行内编辑组件浮现的距离表格容器顶边框的距离
        left: '0px', // 行内编辑组件浮现的距离表格容器左边框的距离
        width: '0px', // 行内编辑组件浮现的宽度（一定等于当前选中单元格的宽度）
        height: '0px', // 行内编辑组件浮现的高度（一定等于当前选中单元格的高度）
        origValue: undefined // 点击单元格编辑生效之前的值
      },
      // 辅助线相关属性（包含提示）
      removeLine: { display: 'none', top: 0, left: 0, info: { display: 'none', top: 0, left: 0 } },
      // 缓存的表格数据 排序、筛选前的状态记录
      cacheTableData: [],
      // 存放表格中被编辑过的行的集合
      editedList: new Set(),
      // 表格数据能否选中 默认false 可选中
      noSelect: false,
      // 表格筛选字段及其值记录 key:field value:tableData[field]
      filterFieldMap: new Map(),
      // 表格筛选下拉字段每次加载数量 - 滚动条下滑到一定程序再次拉取数据，每次加载时的数量
      filterFieldLoadLimitNum: 20,
      // 表格滚动条
      tableScrollVisible: {
        x: false, // X轴 默认hidden
        y: false// Y轴 默认hidden
      },
      // 可视区域高度 - 用于开启表格可视区域自适应做计算
      visibleHeight: null,
      // 用于生产全局唯一key值（已用于：表格）
      onlyKeySet: new Set()
    }
  },
  computed: {
    /**
     * 表格作用域
     * @returns {default.computed}
     */
    tableThis() {
      return this
    },
    /**
     * 加载当前页面需要展示的所有行数据 - 包含 tableData 和 expansionData(展开行数据)
     * @returns {*[]}
     */
    loadAllTableData() {
      const data = [...this.tableData]
      if (data.length > 0 && this.expandable) {
        this.expansionData.forEach(el => {
          // 1.判断是否存在前序兄弟节点 - 存在直接找其最近的上层兄弟，不存在直接找其父节点
          const preNodeData = el._c_meta_.preNodeData ? el._c_meta_.preNodeData : el._c_meta_.parentData
          const index = data.findIndex(item => item._c_meta_.key === preNodeData._c_meta_.key)
          data.splice(index + 1, 0, el)
        })
      }
      return data
    },
    /**
     * 获取表格单元格高度 - 通过表格size决定
     * @returns {number}
     */
    getTableDefaultTDHeightBySize() {
      return this.size === 'large' ? 40 : 36
    },
    /**
     * 获取表格属性
     * @returns
     */
    getTableAttrs() {
      const tableAttrsArr = d.tableAttrs
      tableAttrsArr.forEach(el => { el.forEach(item => { item.value = this.$store.state.table[item.key] }) })
      return {
        tableAttrsArr: tableAttrsArr,
        tableAttrs: tableAttrsArr.flat()
      }
    },
    /**
     * 获取左侧操作栏总宽度
     * @returns {*}
     */
    getLeftSelectionBarWidth() {
      return (this.tableMoveable ? this.defaultTDWidth.moveableTDWidth : 0) +
      (this.selectable ? this.defaultTDWidth.selectableTDWidth : 0) +
      (this.expandable && this.expandCustom ? this.defaultTDWidth.expandableTDWidth : 0) +
      (this.tableShowNumber ? this.defaultTDWidth.numberTDWidth : 0)
    },
    /**
     * 获取标题属性 标题单元格宽度、标题行宽占比
     * @returns {{titles: {left, center, right}, columns: {left: *[], center: *[], right: *[]}}}
     */
    tabTitle() {
      const that = this
      const tableTitle = d.copyObj(this.preTableTitle)
      const columnLeft = []
      const columnCenter = []
      const columnRight = []
      const fixedSet = new Set()
      const nofixedSet = new Set()
      const tableColumns = d.calCols(tableTitle)
      for (const item of tableColumns) {
        // 对没有宽度的列默认为150
        item.width = item.width || 150
        // 对固定列与非固定列进行分类
        if (!item.hidden) {
          if (item.fixed) {
            fixedSet.add(item)
          } else {
            nofixedSet.add(item)
          }
        }
        // 如果是隐藏列,就不放进容器
        if (item.hidden) {
          continue
        }
        // 对参数分组
        if (item.fixed === 'left') {
          columnLeft.push(item)
        } else if (item.fixed === 'right') {
          columnRight.push(item)
        } else {
          columnCenter.push(item)
        }
      }
      that.finishedProcessing = true
      // 进行宽度自适应计算
      const containerWidth = this.tableBoundingClientRect.width - this.barWidth - this.getLeftSelectionBarWidth
      d.calColumnWidth(containerWidth, fixedSet, nofixedSet)
      const tableTitles = d.calSpan(tableTitle)
      return {
        titles: {
          left: d.getGroupTitles(tableTitles, 'left'),
          center: d.getGroupTitles(tableTitles),
          right: d.getGroupTitles(tableTitles, 'right')
        },
        columns: {
          left: columnLeft,
          center: columnCenter,
          right: columnRight
        }
      }
    },
    /**
     * 获取左、中、右三张表标题最底层数组，合并为一个数组，用于元素定位(去除隐藏元素)
     * @returns {*[]}
     */
    getTabTitleArr() {
      const tabTitle = this.tabTitle
      return [...tabTitle.columns.left, ...tabTitle.columns.center, ...tabTitle.columns.right].filter(el => !el.hidden)
    },
    /**
     * 可视标题筛选
     * @returns {{arr: *[], visible: *[], fixedVisible: *[]}}
     */
    visibleTabTitle() {
      const arr = []
      const visible = []
      const fixed = []
      for (const item of this.preTableTitle) {
        if (item.field || item.children) {
          arr.push(item)
          if (item.hidden !== true) {
            visible.push(item.title)
            if (item.fixed === 'left') {
              fixed.push(item.title)
            }
          }
        }
      }
      return {
        arr: arr,
        visible: visible,
        fixedVisible: fixed
      }
    },
    /**
     * 滚动条X轴是否显示，及其显示宽度
     * @returns {number}
     */
    scrollBarWidthX() {
      return this.tableScrollVisible.x ? this.barWidth : 0
    },
    /**
     * 滚动条Y轴是否显示，及其显示宽度
     * @returns {number}
     */
    scrollBarWidthY() {
      return this.tableScrollVisible.y ? this.barWidth : 0
    },
    /**
     * 表格内容高度计算
     * @returns {number|*}
     */
    tableBodyHeight() {
      let height = this.autoCalcVisible ? (this.visibleHeight - this.defaultTableTitleHeight) : this.tableHeight
      if (!height) {
        height = this.tableAutoHeight
      }
      return height
    },
    /**
     * 筛选包含titleAttribute属性的表格
     * @returns {*[]}
     */
    titleAttributeTable() {
      const compareTable = []
      const tabTitle = this.tabTitle
      for (var table in tabTitle.columns) {
        const tableColumns = tabTitle.titles[table][0]
        tableColumns.forEach(el => {
          if (el.titleAttribute) {
            compareTable.push(table)
          }
        })
      }
      return compareTable
    },
    /**
     * 复选框显示内容
     * @returns {string}
     */
    allCheckFont() {
      let font = '&#xe637;'
      switch (this.allCheckState) {
        case 'all':
          font = '&#xe6cb;'
          break
        case 'half':
          font = '&#xe70f;'
          break
        case 'no':
          font = '&#xe637;'
          break
      }
      return font
    },
    /**
     * 获取边框宽度
     * @returns {number}
     */
    getBorderWidth() {
      let borderWidth = 0
      if (this.isTableAttrOn('borderCell')) {
        borderWidth = 1
      }
      if (borderWidth === 1 && this.isTableAttrOn('borderBold')) {
        borderWidth = 2
      }
      return borderWidth
    },
    /**
     * 自适应左侧边距
     * @returns {{left: string, right: string, leftNum: *, rightNum: number}}
     */
    tableFixedMargin: function() {
      let marginLeftWidth = this.getLeftSelectionBarWidth
      for (const item of this.tabTitle.columns.left) {
        const width = item.width || this.defaultTDWidth.otherTDWidth
        marginLeftWidth += width
      }
      if (this.tableShowNumber || this.selectable || this.tableMoveable || this.expandable || this.tabTitle.columns.left.length > 0) {
        marginLeftWidth += this.getBorderWidth
      }
      let marginRightWidth = 0
      for (const item of this.tabTitle.columns.right) {
        const width = item.width || this.defaultTDWidth.otherTDWidth
        marginRightWidth += width
      }
      if (this.tabTitle.columns.right.length > 0) {
        marginRightWidth += this.getBorderWidth
      }
      return {
        left: marginLeftWidth + 'px',
        right: marginRightWidth + 'px',
        leftNum: marginLeftWidth,
        rightNum: marginRightWidth
      }
    },
    /**
     * 解析表格拖拽移动行的提示信息
     * @returns {U[]|*[]}
     */
    parseTableMoveInfo() {
      if (this.tableMoveInfoFields) {
        const tabTitle = [
          ...this.tabTitle.columns.left,
          ...this.tabTitle.columns.center,
          ...this.tabTitle.columns.right
        ]
        return this.tableMoveInfoFields.split(',').map(el => tabTitle.find(item => item.field === el))
      }
      return []
    },
    /**
     * 获取展开树的字段 - 定位展开符号位置
     * @returns {*}
     */
    getExpandTreeFiled() {
      return [...this.tabTitle.columns.left, ...this.tabTitle.columns.center, ...this.tabTitle.columns.right][0]
    }
  },
  mounted() {
    this.initObserve()
    // 加载表格数据
    this.currentParam = this.param
    if (this.autoLoad) {
      this.pageChange(1, this.pageSize)
    }
    this.$nextTick(() => {
      // 表格加载完成后 回调
      this.$emit('tableLoadingReady', this.tableThis)
    })
  },
  methods: {
    /* ---------------------------------父组件可执行操作------------------------------------------ */
    /**
     * 设置表格数据
     * @param list 数据
     * @param total 总数
     */
    setTableData(list, total) {
      // 扩展行初始化
      this.expansionData = []
      this.total = total
      this.tableData = list
    },
    /**
     * 添加一行数据
     * @param row 行数据
     * @param isLast 是否添加在末尾
     */
    addRow(row, isLast) {
      Object.defineProperties(row, {
        cEdited: {
          value: {},
          writable: true,
          enumerable: false
        },
        cAdded: {
          value: true,
          writable: false,
          enumerable: false
        }
      })
      for (const key in row) {
        row.cEdited[key] = true
      }
      if (isLast) {
        this.tableData.push(row)
      } else {
        this.tableData.splice(0, 0, row)
      }
      this.editedList.add(row)
      this.$nextTick(function() {
        this.$emit('update:current-row', row)
      })
    },
    /**
     * 删除一行数据
     * @param row 表格某行的数据
     * @param callback 回调函数
     */
    deleteRow(row, callback) {
      for (const index in this.tableData) {
        if (this.tableData[index] === row) {
          this.tableData.splice(index, 1)
          break
        }
      }
      if (row.cAdded) {
        this.editedList.delete(row)
      }
      if (callback) {
        callback(row)
      }
    },
    /**
     * 获取表格中被更新过的数据
     * 修改这里的数据会同步到表格上
     * @returns {*[]}
     */
    getUpdate() {
      const arr = []
      for (const item of this.editedList) {
        if (!item.cAdded) {
          arr.push(item)
        }
      }
      return arr
    },
    /**
     * 获取表格中被更新过的数据用于提交到接口,转json,去除组件参数
     * 修改这里的数据不会同步到表格上
     * @returns {*[]}
     */
    getUpdateJson() {
      const arr = []
      for (const item of this.editedList) {
        if (!item.cAdded) {
          arr.push(item)
        }
      }
      const deepCopyArray = JSON.parse(JSON.stringify(arr));
      deepCopyArray.forEach(item=>{
        delete item._c_meta_
      })
      return deepCopyArray
    },
    /**
     * 获取表格中新增的数据
     * 修改这里的数据会同步到表格上
     * @returns {*[]}
     */
    getInsert() {
      const arr = []
      for (const item of this.editedList) {
        if (item.cAdded === true) {
          arr.push(item)
        }
      }
      return arr
    },
    /**
     * 获取表格中新增的数据用于提交到接口,转json,去除组件参数
     * 修改这里的数据不会同步到表格上
     * @returns {*[]}
     */
    getInsertJson() {
      const arr = []
      for (const item of this.editedList) {
        if (item.cAdded === true) {
          arr.push(item)
        }
      }
      const deepCopyArray = JSON.parse(JSON.stringify(arr));
      deepCopyArray.forEach(item=>{
        delete item._c_meta_
      })
      return deepCopyArray
    },
    /**
     * 获取表格中发生改变过的数据，即新增、修改过的所有数据
     * 修改这里的数据会同步到表格上
     * @returns {*[]}
     */
    getModify() {
      const insert = this.getInsert()
      const update = this.getUpdate()
      return insert.concat(update)
    },
    /**
     * 获取表格中发生改变过的数据用于提交到接口,转json,去除组件参数
     * 修改这里的数据不会同步到表格上
     * @returns {*[]}
     */
    getModifyJson() {
      const insert = this.getInsertJson()
      const update = this.getUpdateJson()
      return insert.concat(update)
    },
    /**
     * 获取表格中多选框选中数据
     * @returns {*|*[]}
     */
    getSelected() {
      const that = this
      const arr = this.getCurPageSelected()
      if (this.rowKey) {
        return d.copyObj(Array.from(this.selectedMap.values()))
      } else {
        return arr
      }
    },
    /**
     * 获取表格中复选框选中后又取消选中的数据
     * @returns {any[]}
     */
    getSelectedDeleteData() {
      return Array.from(this.selectedDeleteMap.values())
    },
    /**
     * 清空表格中多选框已选中的数据
     */
    clearSelected() {
      this.selectedMap = new Map()
      this.setCheckBox(!(this.expandable && this.expandCustom) ? this.loadAllTableData : this.tableData, false)
    },
    /**
     * 清空重置编辑过的数据
     */
    resetEdit() {
      this.editedList = new Set()
    },
    /**
     * 加载表格数据
     * @param obj 参数
     * @param callback 回调函数，会将查询的数据带入
     */
    load(obj, callback) {
      this.resetEdit()
      this.currentParam = obj
      this.pageChange(1, this.pageSize, callback)
    },
    /**
     * 重新加载表格数据
     * @param callback 回调函数，会将查询的数据带入
     */
    reload(callback) {
      this.resetEdit()
      this.pageChange(this.pageNum, this.pageSize, callback)
    },
    /**
     * 获取表格内部数据缓存 如 selector tree等
     * @param url 请求的url
     * @param callback 回调
     */
    getSelectData(url,callback) {
      const cacheItem = this.dicCacheMap.get(url)
      if (cacheItem) {
        callback(cacheItem)
      } else {
        this.$getAction(url).then(res => {
          this.dicCacheMap.set(url, res.data)
          callback(res.data)
        })
      }
    },
    /**
     * 获取表格所有数据
     * 修改这里的数据会同步到表格上
     * @returns {*[]}
     */
    getTableData() {
      return this.loadAllTableData.filter(el => !el._c_meta_ || !el._c_meta_.parentData)
    },
    /**
     * 获取表格所有数据用于提交到接口,转json,去除组件参数
     * 修改这里的数据不会同步到表格上
     * @returns {*[]}
     */
    getTableDataJson() {
      let allData=[...this.loadAllTableData.filter(el => !el._c_meta_ || !el._c_meta_.parentData)]
      const deepCopyArray = JSON.parse(JSON.stringify(allData));
      deepCopyArray.forEach(item=>{
        delete item._c_meta_
      })
      return deepCopyArray
    },
    /**
     * 清除表格编辑状态
     */
    clearEditor(){
      let data = []
      for (const item of this.editedList) {
        data.push(item)
      }
      data.filter(el => el._c_meta_).map(el=>{
        const propertyNames = [];
        for (const key in el._c_meta_) {
          if(key.startsWith('edit_icon_')){
            propertyNames.push(key)
          }
        }
        propertyNames.forEach(item=>{
          delete el._c_meta_[item]
        })
      })
      let allData=[...this.loadAllTableData.filter(el => !el._c_meta_ || !el._c_meta_.parentData)]
      this.tableData = allData
      this.editItemBlur()
    },
    /**
     * 设置当前选中行
     * @param row
     */
    setCurrentRow(row) {
      this.currentRow = row
    },
    /**
     * 手动设置列是否可编辑
     * @param isEdit true 是 false 否
     * @param arr 包含的列，不传则设置全部
     */
    setEditState(isEdit, arr) {
      const that = this
      const tableTitle = d.copyObj(this.preTableTitle)
      this.forEachFieldItem(tableTitle, item => {
        if (arr) {
          if (arr.includes(item.field)) {
            item.edit = isEdit
          }
        } else {
          item.edit = isEdit
        }
        if (!item.type) {
          item.type = 'text'
        }
        if (item.type === 'selector') {
          preHandler.selector(that, item)
        } else if (item.type === 'tree') {
          preHandler.tree(that, item)
        } else if (item.type === 'number') {
          preHandler.number(that, item)
        } else if (item.type === 'click') {
          preHandler.click(that, item)
        }
      })
      this.preTableTitle = tableTitle
    },
    /**
     * 更新表格行数据
     * @param index 行索引
     * @param row 数据
     */
    updateRow(index, row) {
      if (!this.tableData[index]) {
        console.error('参数index[' + index + ']有误，无法在表格数据中找到对应数据！')
        return
      }
      Object.defineProperties(row, {
        cEdited: {
          value: this.tableData[index].cEdited || {},
          writable: true,
          enumerable: false
        },
        cAdded: {
          value: this.tableData[index].cAdded,
          writable: false,
          enumerable: false
        }
      })
      // 给值有改变的项加上修改标记
      for (const key in this.tableData[index]) {
        if (this.tableData[index][key] !== row[key]) {
          row.cEdited[key] = true
        }
      }
      // 删除原来编辑列表中的项，把新修改的项加进去
      this.editedList.delete(this.tableData[index])
      this.$set(this.tableData, index, row)
      this.editedList.add(row)
    },
    /**
     * 所有编辑框失去焦点
     */
    editItemBlur() {
      this.tableThis.editor.type = undefined
    },
    /**
     * 表格数据非空校验，校验字段中notNull:true的字段
     * @param callBack
     * @returns {boolean}
     */
    checkNotNull(callBack) {
      let isNullFlag = false
      const msgArr = []
      const tabTitle = this.tabTitle.titles
      const notNullTitles = [...tabTitle.left, ...tabTitle.center, ...tabTitle.right].flat().filter(el => el.notNull)
      if (notNullTitles.length > 0) {
        const tableData = this.getTableData()
        notNullTitles.forEach(el => {
          tableData.forEach(item => {
            if (!item[el.field] || item[el.field] === '') {
              isNullFlag = true
              msgArr.push(el.title)
            }
          })
        })
      }
      // 非空提示
      if (isNullFlag) {
        if (callBack) {
          callBack(msgArr)
        } else {
          this.$message.warning('表格：' + msgArr.join('、') + ' 不能为空！')
        }
      }
      return isNullFlag
    },
    /* ---------------------------------组件内部方法------------------------------------------ */
    /**
     * 监听容器大小，自动调整宽，高，滚动条
     */
    initObserve(){
      const _this = this
      // 监听容器大小
      const rootRo = new ResizeObserver(entries => {
        if (this.autoCalcVisible) {
          // 自动计算：当前可视区域高度 = 当前页面可视区域高度-表格距离顶部高度-页脚高度-预留
          const rect = this.$el.getBoundingClientRect()
          const visibleHeight = document.body.clientHeight - rect.top - 84 - 2
          this.visibleHeight = visibleHeight > 200 ? visibleHeight : 200
        }
        for (const entry of entries) {
          const cr = entry.contentRect
          let autoHeight = cr.height - _this.defaultTableTitleHeight * _this.tabTitle.titles.left.length
          if (this.$refs.cparams && this.$refs.cparams.$el.nodeName === 'DIV') {
            autoHeight -= this.$refs.cparams.$el.getBoundingClientRect().height
          }
          if (this.pageable) {
            autoHeight -= (this.defaultPageHeight + 5)
          }
          _this.tableAutoHeight = autoHeight
          _this.tableBoundingClientRect = {
            width: _this.$el.clientWidth,
            height: _this.$el.clientHeight,
            left: _this.$el.getBoundingClientRect().left,
            right: _this.$el.getBoundingClientRect().right
          }
        }
      })
      rootRo.observe(this.$el)
      const tableRo = new ResizeObserver(entries => {
        this.autoAdaptTRHeight()
        _this.autoLoadBar()
      })
      tableRo.observe(this.$refs.contentTable)
      // 监听 滚动条X、Y轴是否显示判断
      const centerTableRo = new ResizeObserver(entries => {
        _this.autoLoadBar()
      })
      centerTableRo.observe(this.$refs.centerTableBodyMain)
      if (this.titleAttributeTable.length > 0) {
        this.fixAutoHeight()
      }
    },
    /**
     * 自动加载滚动条
     */
    autoLoadBar() {
      const _this = this
      const centerTable = _this.$refs.centerTableBodyMain
      if (centerTable) {
        const centerTableRect = centerTable.getBoundingClientRect()
        const parentDivRect = centerTable.parentNode.getBoundingClientRect()
        _this.tableScrollVisible.y = centerTableRect.height > parentDivRect.height
        _this.tableScrollVisible.x = (centerTableRect.width + _this.tableFixedMargin.leftNum) > parentDivRect.width
        // 自动计算滚动条宽度 - 需等待滚动条加载完成后计算
        _this.reCountBarWidth()
      }
    },
    /**
     * 重新计算滚动条距离 - 当页面筛选未打开时
     */
    reCountBarWidth() {
      const _this = this
      _this.$nextTick(() => {
        const tabTitle = _this.tabTitle.titles
        const tabTitleArr = [...tabTitle.left.flat(), ...tabTitle.center.flat(), ...tabTitle.right.flat()].filter(el => el.filter)
        const cr = _this.$refs.centerTableBody
        if (cr && !tabTitleArr.find(el => el.filterDropdownVisible)) {
          _this.barWidth = cr.offsetWidth - cr.clientWidth
        }
      })
    },
    /**
     * 表格标题筛选数据数据集输入框条件查询
     * @param e
     * @param fieldInfo
     */
    tableTitleFieldFilterArrSearch(e, fieldInfo) {
      this.$set(fieldInfo, 'filterDropdownSearchText', e.target.value)
      this.$forceUpdate()
    },
    /**
     * 获取表格标题筛选数据数据集
     * @param fieldInfo
     * @returns {Set<string>}
     */
    getTableTitleFieldFilterArr(fieldInfo) {
      let arr = (this.cacheTableData.length === 0 ? [...this.tableData] : [...this.cacheTableData]).map(el => this.formatExcelCellValue(fieldInfo, el))
      if (fieldInfo.filterDropdownSearchText && fieldInfo.filterDropdownSearchText.length > 0) {
        arr = arr.filter(el => el && el.indexOf(fieldInfo.filterDropdownSearchText) > -1)
      }
      return new Set(arr)
    },
    /**
     * 获取表格标题筛选数据数据集 - 限制显示数量 - 初始化dom时，保障数据量过大时快速加载数据
     * @param fieldInfo
     * @returns {string[]}
     */
    getTableTitleFieldFilterArrLimit(fieldInfo) {
      const limitNum = (fieldInfo.filterDropdownLoadMore ? fieldInfo.filterDropdownLoadMore : 1) * this.filterFieldLoadLimitNum
      const arr = Array.from(this.getTableTitleFieldFilterArr(fieldInfo)).filter((el, index) => index < limitNum)
      return arr
    },
    /**
     * 表格标题筛选数据数据集 滚动条滚动-动态加载数据
     * @param e
     * @param fieldInfo
     */
    tableTitleFieldFilterScroll(e, fieldInfo) {
      // 内容区域总高度
      const totalHeight = e.srcElement.firstChild.clientHeight
      // 内容区域可是高度
      const relativeHeight = e.srcElement.clientHeight
      // 滚动条 滚动距离
      const scrollTop = e.srcElement.scrollTop
      // 当滚动条滚动到底部距离 < 100 时，并且当前可视数据小于总数据时，动态加载可视数据
      if ((totalHeight - scrollTop - relativeHeight) < 100 && (fieldInfo.filterDropdownLoadMore ? fieldInfo.filterDropdownLoadMore : 1) * this.filterFieldLoadLimitNum < this.getTableTitleFieldFilterArr(fieldInfo).size) {
        this.$set(fieldInfo, 'filterDropdownLoadMore', fieldInfo.filterDropdownLoadMore ? fieldInfo.filterDropdownLoadMore + 1 : 2)
        this.$forceUpdate()
      }
    },
    /**
     * 表格标题筛选数据数据集 选择框状况勾选触发
     * @param e
     * @param value
     * @param field
     */
    tableTitleFieldFilterChecked(e, value, field) {
      let arr = this.filterFieldMap.get(field)
      if (!arr) {
        arr = new Set()
      }
      if (e.target.checked) {
        arr.add(value)
      } else {
        arr.delete(value)
      }
      this.filterFieldMap.set(field, arr)
      // 将原始数据先进行排序，在进行筛选
      this.sortOrFilterRollback()
    },
    /**
     * 表格标题筛选数据数据集 选择框-全选操作
     * @param fieldInfo
     */
    tableTitleFieldFilterCheckedAll(fieldInfo) {
      const field = fieldInfo.field
      let arr = this.filterFieldMap.get(field)
      if (!arr) {
        arr = new Set()
      }
      let arrAfterSearchLength = arr.size
      const getTableTitleFieldFilterArr = this.getTableTitleFieldFilterArrLimit(fieldInfo)
      if (fieldInfo.filterDropdownSearchText && fieldInfo.filterDropdownSearchText.length > 0) {
        arrAfterSearchLength = Array.from(arr).filter(el => el && el.indexOf(fieldInfo.filterDropdownSearchText) > -1).length
      }
      // 是否全部选中 即判断当前选中数据长度是否和加载的数据长度一致 一致：即全不选中，不一致徐全选中 (根据搜索框内容检索)
      if (arrAfterSearchLength === getTableTitleFieldFilterArr.length) {
        getTableTitleFieldFilterArr.forEach(el => { arr.delete(el) })
      } else {
        getTableTitleFieldFilterArr.forEach(el => { arr.add(el) })
      }
      this.filterFieldMap.set(field, arr)
      // 将原始数据先进行排序，在进行筛选
      this.sortOrFilterRollback()
    },
    /**
     * 表格标题筛选数据数据集 选择框-全选状态显示
     * @param indeterminate
     * @param fieldInfo
     * @returns {boolean}
     */
    tableTitleFieldFilterCheckedAllStatus(indeterminate, fieldInfo) {
      let arr = this.filterFieldMap.get(fieldInfo.field)
      if (arr && arr.size > 0) {
        arr = Array.from(arr)
        const getTableTitleFieldFilterArr = this.getTableTitleFieldFilterArrLimit(fieldInfo)
        if (fieldInfo.filterDropdownSearchText && fieldInfo.filterDropdownSearchText.length > 0) {
          arr = arr.filter(el => el && el.indexOf(fieldInfo.filterDropdownSearchText) > -1)
        }
        return indeterminate ? arr.length < getTableTitleFieldFilterArr.length : arr.length === getTableTitleFieldFilterArr.length
      }
      return false
    },
    /**
     * 下拉弹框隐藏、显示
     * @param fieldInfo
     * @param visible
     */
    toggleFilterDropdown(fieldInfo, visible) {
      const tabTitle = this.tabTitle.titles
      const tabTitleArr = [...tabTitle.left.flat(), ...tabTitle.center.flat(), ...tabTitle.right.flat()].filter(el => el.filter)
      tabTitleArr.forEach(el => this.$set(el, 'filterDropdownVisible', false))
      this.$set(fieldInfo, 'filterDropdownVisible', visible)
      this.$forceUpdate()

      if (!visible) {
        this.reCountBarWidth()
      }
    },
    /**
     * 筛选条件清空
     */
    filterFieldMapClear() {
      this.filterFieldMap.clear()
    },
    /**
     * 展开折叠数据
     * @param e
     * @param dataItem
     * @param expand
     */
    rowExpansion(e, dataItem, expand) {
      this.composedPath(e)
      this.setCurrentRow(dataItem)
      this.$set(dataItem._c_meta_, 'expandHide', expand)
      // 构建可展开行数据
      let expansionData = [...this.expansionData]
      if (!this.expandCustom) {
        // 1.1.不使用自定义模式 - 默认直接加载树形数据
        dataItem.children.forEach((el, index) => {
          if (expand) {
            el._c_meta_ = {
              key: this.getOnlyKey(),
              level: dataItem._c_meta_.level + 1, // 层级
              paddingLeft: this.defaultExpandTreeMarginRight * dataItem._c_meta_.level, // 距离左侧位置
              expandIcon: !!el.children,
              parentData: dataItem,
              preNodeData: index > 0 ? dataItem.children[index - 1] : null
            }
            expansionData.push(el)
          } else {
            // 1.2.展开行 - 针对不使用自定义展开时，即树形展开，在取消展开后，需要删除其子节点的所有元素
            const diedai = (el) => {
              if (el._c_meta_ && el._c_meta_.key) {
                expansionData = expansionData.filter(item => item._c_meta_.key !== el._c_meta_.key)
                if (el.children) {
                  el.children.forEach((item) => { diedai(item) })
                }
              }
            }
            diedai(el)
          }
        })
        this.expansionData = [...expansionData]
      } else {
        // 获取当前点击行的底部距离
        const domBottomDistance = this.reloadExpansionData(e.path.find(el => el.tagName === 'TR'))
        if (expand) {
          const parentData = JSON.parse(JSON.stringify(dataItem))
          delete parentData._c_meta_.expandRow
          dataItem._c_meta_.expandRow = {
            key: this.getOnlyKey(),
            top: domBottomDistance,
            left: this.$refs.centerTableHead.scrollLeft,
            expandRowHeight: this.expandRowHeight,
            expandIcon: false,
            parentData: parentData
          }
          expansionData.push({ _c_meta_: dataItem._c_meta_.expandRow })
          this.expansionData = [...expansionData]
          this.$nextTick(() => {
            this.expansionData.forEach(el => { if (el._c_meta_.top > domBottomDistance) { el._c_meta_.top += this.$refs['expand_row_' + dataItem._c_meta_.expandRow.key][0].getBoundingClientRect().height - 2 * (this.isTableAttrOn('borderBold') ? 2 : 1) } })
          })
        } else {
          expansionData = expansionData.filter(item => item._c_meta_.key !== dataItem._c_meta_.expandRow.key)
          expansionData.forEach(el => { if (el._c_meta_.top > domBottomDistance) { el._c_meta_.top -= this.$refs['expand_row_' + dataItem._c_meta_.expandRow.key][0].getBoundingClientRect().height - 2 * (this.isTableAttrOn('borderBold') ? 2 : 1) } })
          this.expansionData = [...expansionData]
        }
      }
      this.autoAdaptTRHeight()
    },
    /**
     * 自动加载行展开内容位置
     * @param dom
     * @returns {number}
     */
    reloadExpansionData(dom) {
      return dom.getBoundingClientRect().bottom - this.$refs.contentTable.getBoundingClientRect().top - (this.isTableAttrOn('borderBold') ? 2 : 1) + this.scrollTop
    },
    /**
     * 判断表格指定属性是否存在
     * @param key
     * @returns {boolean|*}
     */
    isTableAttrOn(key) {
      const attr = this.getTableAttrs.tableAttrs.find(el => el.key === key)
      return attr ? attr.value : false
    },
    /**
     * 获取边框信息
     * @param borderCell
     * @returns {string}
     */
    getBorderStyle(borderCell) {
      let border = 'none'
      if (borderCell && this.isTableAttrOn('borderBold')) {
        border = this.getBorderWidth + 'px solid #E5E6EB'
      } else if (borderCell) {
        border = this.getBorderWidth + 'px solid #E5E6EB'
      }
      return border
    },
    /**
     * 自动适应 tr 行高
     */
    autoAdaptTRHeight() {
      this.$nextTick(() => {
        // 监听表格内部区域的所有TD的高度，自动等高
        const ctrs = this.$refs.ctr
        if (ctrs) {
          this.loadAllTableData.forEach((el, index) => {
            let expandRow = false
            el._c_meta_ = el._c_meta_ ? el._c_meta_ : {}
            let maxHeight = el._c_meta_.height ? el._c_meta_.height : 0
            if (this.expandCustom && el._c_meta_.parentData) {
              const rows = this.$refs['expand_row_' + el._c_meta_.key]
              if (rows && rows.length > 0) {
                expandRow = true
                maxHeight = rows[0].getBoundingClientRect().height - 2 * (this.isTableAttrOn('borderBold') ? 2 : 1)
              }
            }
            if (!expandRow) {
              ctrs.forEach(item => {
                if (item.attributes.rowIndex && parseInt(item.attributes.rowIndex.nodeValue) === index) {
                  const trHeight = item.getBoundingClientRect().height
                  if (trHeight > maxHeight) {
                    maxHeight = trHeight
                  }
                }
              })
            }
            el._c_meta_.height = maxHeight > this.getTableDefaultTDHeightBySize ? maxHeight : this.getTableDefaultTDHeightBySize
          })
          this.$forceUpdate()
        }
      })
    },
    /**
     * 判断当前是否使用titleAttribute规则
     * @param columns
     * @param field
     * @param minWidth
     * @returns {boolean}
     */
    titleCombineByWidth(columns, field, minWidth) {
      let flag = false
      for (let i = 0; i < columns.length; i++) {
        const el = columns[i]
        if (el.field === field && el.width < minWidth) {
          flag = true
          break
        }
      }
      return flag
    },
    /**
     * 初始化列拖动时调用，表格高度自适应titleAttribute存在，且高度最大表格
     */
    fixAutoHeight() {
      const obj = {}
      let tableRefName = ['leftTableHead', 'centerTableHead', 'rightTableHead']
      let maxHeight = 0
      let currentAutoTable = ''
      // 根据titleAttributeTable获取需要比较高度的表格
      for (const preTable of this.titleAttributeTable) {
        const tableName = preTable + 'TableHead'
        const tableRef = this.$refs[tableName]
        if (tableRef) {
          tableRef.getElementsByTagName('tr').forEach(el => {
            if (maxHeight < el.clientHeight) {
              maxHeight = el.clientHeight
              currentAutoTable = tableName
            }
          })
        }
      }
      tableRefName = tableRefName.filter(el => el !== currentAutoTable)
      tableRefName.forEach(el => {
        obj[el] = { height: maxHeight + 'px' }
      })
      this.titleTheadClass = obj
    },
    /**
     * 单元格内容格式化
     * @param fieldItem
     * @param dataItem
     * @returns {*|string}
     */
    formatTd(fieldItem, dataItem) {
      let value = ''
      if (this.expandable && this.expandCustom && dataItem._c_meta_.parentData) {
        return value
      }
      if (fieldItem.type === 'dynamic') {
        const tempFieldInfo = fieldItem.dynamic(this, dataItem)
        if (tempFieldInfo && tempFieldInfo.formater) {
          value = tempFieldInfo.formater(dataItem[fieldItem.field], dataItem)
        } else {
          value = dataItem[fieldItem.field]
        }
      } else if (fieldItem.formater && typeof fieldItem.formater === 'function') {
        value = fieldItem.formater(dataItem[fieldItem.field], dataItem)
      } else {
        value = dataItem[fieldItem.field]
      }
      if (fieldItem.type === 'switch') {
        if (fieldItem.formater) {
          const v = fieldItem.formater(dataItem[fieldItem.field], dataItem)
          value = v ? '&#xe643;' : '&#xe642;'
        } else {
          const on = fieldItem.on ? fieldItem.on : true
          value = value === on ? '&#xe643;' : '&#xe642;'
        }
      } else if (fieldItem.type === 'checkBox') {
        if (fieldItem.formater) {
          const v = fieldItem.formater(dataItem[fieldItem.field], dataItem)
          value = v ? '&#xe710;' : '&#xe711;'
        } else {
          const on = fieldItem.on ? fieldItem.on : true
          value = value === on ? '&#xe710;' : '&#xe711;'
        }
      }
      return value
    },
    /**
     * 单元格提示标题格式化
     * @param fieldItem
     * @param dataItem
     * @returns {*|string}
     */
    formatTdTitle(fieldItem, dataItem) {
      let value = ''
      if (this.expandable && this.expandCustom && dataItem._c_meta_.parentData) {
        return value
      }
      if (fieldItem.type === 'switch' || fieldItem.type === 'checkBox') {
        value = ''
      } else if (fieldItem.formater && typeof fieldItem.formater === 'function') {
        value = fieldItem.formater(dataItem[fieldItem.field], dataItem)
      } else {
        value = dataItem[fieldItem.field]
      }
      return value
    },
    /**
     * 判断单元格是否可编辑
     * @param fieldItem
     * @param dataItem
     * @returns {boolean}
     */
    isEdited(fieldItem, dataItem) {
      if (dataItem.cEdited) {
        if (dataItem.cEdited[fieldItem.field]) {
          return true
        }
      }
      return false
    },
    /**
     * 获取单元格自定义样式
     * @param fieldItem
     * @param dataItem
     * @returns {{}}
     */
    getStyle(fieldItem, dataItem) {
      let css = {}
      if (fieldItem.type === 'switch' || fieldItem.type === 'checkBox') {
        return css
      }
      if (fieldItem.align) {
        css = { 'text-align': fieldItem.align }
      }
      if (fieldItem.style) {
        css = { ...css, ...fieldItem.style(dataItem[fieldItem.field], dataItem) }
      }
      return css
    },
    /**
     * 勾选框是否选中
     * @param fieldItem
     * @param dataItem
     * @returns {boolean}
     */
    isSelected(fieldItem, dataItem) {
      let selected = false
      const value = dataItem[fieldItem.field]
      if (fieldItem.type === 'switch' || fieldItem.type === 'checkBox') {
        const on = fieldItem.on ? fieldItem.on : true
        selected = value === on
      }
      return selected
    },
    /**
     * 是否是勾选框
     * @param fieldItem
     * @param dataItem
     * @returns {boolean}
     */
    isAliFont(fieldItem) {
      if (fieldItem.type === 'switch' || fieldItem.type === 'checkBox') {
        return true
      }
      return false
    },
    /**
     * 设置标题行复选框状态
     */
    calAllChecked() {
      let checkedCount = 0
      const tableData = !(this.expandable && this.expandCustom) ? this.loadAllTableData : this.tableData
      for (const item of tableData) {
        if (item.cChecked) {
          checkedCount++
        }
      }
      let checkStatus = 'no'
      if (checkedCount === tableData.length) {
        checkStatus = 'all'
      } else if (checkedCount > 0 && checkedCount < tableData.length) {
        checkStatus = 'half'
      }
      this.allCheckState = checkStatus
    },
    /**
     * 设置复选框选中
     * @param arr 表格行数据
     * @param status 是否选中
     */
    setCheckBox(arr, status) {
      for (const row of arr) {
        if (!row.hasOwnProperty('cChecked')) {
          Object.defineProperties(row, {
            cChecked: {
              value: false,
              writable: true,
              enumerable: false
            }
          })
        }
        if (this.rowKey && !status) {
          this.selectedMap.delete(row[this.rowKey])
        }else{
          this.selectedMap.set(row[this.rowKey], row)
        }
        this.$set(row, 'cChecked', status)
      }
      this.calAllChecked()
      this.$emit('selectedChange',arr)
      this.$forceUpdate()
    },
    /**
     * 设置选中数据
     * @param keyArr 主键值
     * @param key 主键字段名称，这个值一定要在rowKey中传入
     * @param status 是否选中
     * @param type 主键值类型，一般不传入,可以传Number
     */
    setCheckBoxByPrimaryKey(keyArr, key, status, type) {
      if (typeof keyArr === 'string') {
        if (keyArr.indexOf(',') > -1) {
          keyArr = keyArr.split(',')
        } else {
          keyArr = keyArr.length > 0 ? [keyArr] : []
        }
      }
      const mp = d.arrToMap(this.tableData, key)
      const arr = []
      keyArr = [...keyArr, ...Array.from(this.selectedKeysCacheSet)]
      for (const item of keyArr) {
        const v = mp.get(item)
        if (type === Number) {
          mp.get(parseInt(item))
        }
        if (v) {
          arr.push(v)
        }
      }
      this.setCheckBox(arr, status)
      // 存放外部设置已选中数据，但selectedMap中暂时不存在的数据
      if (keyArr.length > 0) {
        const selectedAllKeys = [...this.getCurPageSelected().map(el => el[key]), ...this.selectedMap.keys()]
        this.selectedKeysCacheSet = new Set(keyArr.filter(el => !selectedAllKeys.find(item => item === el)))
      }
    },
    /**
     * 复原排序、筛选数据操作前的数据
     */
    sortOrFilterRollback() {
      // 1.缓存原始数据 - 第一次 触发 排序 、 筛选情况下
      if (this.cacheTableData.length === 0) {
        this.cacheTableData = [...this.tableData]
      }
      // 2.所有数据重置为原始数据 用原始数据进行排序 none
      this.tableData = [...this.cacheTableData]
      // 3.定位需要排序字段 - 找第一个currentSortType！==none的字段
      const tabTitle = this.tabTitle.titles
      const tabTitleArr = [...tabTitle.left.flat(), ...tabTitle.center.flat(), ...tabTitle.right.flat()]
      const fieldInfo = tabTitleArr.filter(el => el.sort).find(el => el.currentSortType && el.currentSortType !== 'none')
      // 4.排序 asc、desc
      if (fieldInfo) {
        if (fieldInfo.currentSortType === 'asc') {
          d.sortAsc(this, fieldInfo)
        } else if (fieldInfo.currentSortType === 'desc') {
          d.sortDesc(this, fieldInfo)
        }
      }
      // 5.进行筛选
      let filterArrIsEmpty = true
      let tableData = [...this.tableData]
      for (const key of this.filterFieldMap.keys()) {
        if (this.filterFieldMap.get(key).size > 0) {
          const cacheData = []
          tableData.forEach(el => {
            const target = cacheData.find(item => item._c_meta_.key === el._c_meta_.key)
            if (this.filterFieldMap.get(key).has(this.formatExcelCellValue(tabTitleArr.find(item => item.field === key), el)) && !target) {
              cacheData.push(el)
            }
          })
          tableData = cacheData
        }
        if (filterArrIsEmpty) {
          filterArrIsEmpty = !(this.filterFieldMap.get(key).size > 0)
        }
      }
      if (tableData.length !== this.tableData.length) {
        this.tableData = [...tableData]
      }
      // 缓存数据清空 - 当没有排序与筛选时
      if (!fieldInfo && filterArrIsEmpty) {
        this.cacheTableData = []
      }
      this.$forceUpdate()
    },
    /**
     * 表格标题点击事件
     * @param e
     * @param tabType
     */
    tableThClick(e, tabType) {
      this.editor.type = undefined
      if (e.target.offsetWidth - e.offsetX <= 5) {
        // console.log('标题宽度调节！')
        return
      }
      if (!e.target.attributes.ri && !e.target.attributes.ty) {
        // 无处理事件元素
        return
      }
      // 获取当前事件对应的表头数据
      let tabTitle = this.tabTitle.titles
      const tabSortTitles = [...tabTitle.left.flat(), ...tabTitle.center.flat(), ...tabTitle.right.flat()].filter(el => el.sort)
      if (tabType === 'left') {
        tabTitle = tabTitle.left
      } else if (tabType === 'right') {
        tabTitle = tabTitle.right
      } else {
        tabTitle = tabTitle.center
      }
      // 判断种类ty（序号no、thasc正序排序、thdesc倒序排序）
      const type = e.target.attributes.ty
      if (type) {
        switch (type.nodeValue) {
          case 'no':
            break
          case 'sort':
            if (!this.tableSort) {
              break
            }
            const fieldInfo = tabTitle[e.target.attributes.ri.nodeValue][e.target.attributes.fi.nodeValue]
            if (!fieldInfo.field || fieldInfo.children || !fieldInfo.sort) {
              break
            }
            // 重置其他的排序状态
            tabSortTitles.forEach(el => {
              if (el.field !== fieldInfo.field && fieldInfo.sort) {
                el.currentSortType = 'none'
              }
            })
            // 设置排序状态
            if (!fieldInfo.currentSortType || fieldInfo.currentSortType === 'none') {
              fieldInfo.currentSortType = 'asc'
            } else if (fieldInfo.currentSortType === 'asc') {
              fieldInfo.currentSortType = 'desc'
            } else if (fieldInfo.currentSortType === 'desc') {
              fieldInfo.currentSortType = 'none'
            }
            // 复原数据
            this.sortOrFilterRollback()
            break
          case 'allCheckbox':
            const state = this.allCheckState !== 'all'
            this.setCheckBox((!this.expandable || (this.expandable && this.expandCustom)) ? this.tableData : this.loadAllTableData, state)
            break
        }
      }
    },
    /**
     * 单击表格内的单元格，设置当前选中行，切换开关，复选框组件值
     * @param e
     * @param tabType
     */
    singleClick(e, tabType) {
      this.composedPath(e)
      this.editor.type = undefined
      if (!e.target.attributes.ri && !e.target.attributes.ty) {
        // console.log("无处理事件元素");
        return
      }
      // 获取当前事件对应的表头数据
      let tabTitle = this.tabTitle.columns
      if (tabType === 'left') {
        tabTitle = tabTitle.left
      } else if (tabType === 'right') {
        tabTitle = tabTitle.right
      } else {
        tabTitle = tabTitle.center
      }
      const node = e.target.attributes.ri.nodeValue
      const row = this.loadAllTableData[node]
      // 通知组件更新当前行的值
      this.currentRow = row
      this.$emit('singleClick', row)
      // 自定义展开行、视图模式 - 只能设置当前行 其余禁止
      if ((this.expandable && this.expandCustom && this.currentRow._c_meta_.parentData) || this.viewMode) {
        return
      }
      // 判断是否存在rty属性，有该属性表示特殊列，如序号列、多选框列
      if (e.target.attributes.rty) {
        const rty = e.target.attributes.rty.nodeValue
        if (rty === 'checkbox' || rty === 'no') {
          this.setCheckBox([row], !row.cChecked)
        }
        return
      }
      // 获得列数据的信息
      const fi = e.target.attributes.fi.nodeValue
      const fieldInfo = tabTitle[fi]
      if (d.editEnable(fieldInfo.edit, row)) {
          this.recordEditorIconPosition(row, fieldInfo, e)
          switch (fieldInfo.type) {
            case 'switch':
              d.switchTrigger(this, e, row, fieldInfo, tabTitle)
              break
            case 'checkBox':
              d.checkBoxTrigger(this, e, row, fieldInfo, tabTitle)
              break
          }
        }
      if (!fieldInfo.edit && this.selectable) {
        this.setCheckBox([row], !row.cChecked)
      }
    },
    /**
     * 双击表格内的单元格，编辑单元格数据，根据字段类型弹出下拉，日期等组件，向上层触发双击事件
     * @param e
     * @param tabType
     */
    doubleClick(e, tabType) {
      e.preventDefault()
      this.composedPath(e)
      // 排序双击序号、多选框的双击操作，其他列可双击，返回结果请自行筛选
      if (!e.target.attributes.rty && e.target.attributes.fi) {
        // 获得列数据的信息
        let tabTitle = this.tabTitle.columns
        if (tabType === 'left') {
          tabTitle = tabTitle.left
        } else if (tabType === 'right') {
          tabTitle = tabTitle.right
        } else {
          tabTitle = tabTitle.center
        }
        const fi = e.target.attributes.fi.nodeValue
        const fieldInfo = tabTitle[fi]
        this.$emit('doubleClick', { currentRow: this.currentRow, filedInfo: fieldInfo })
        // 视图模式 不允许编辑
        if (this.viewMode) {
          return
        }
        //给目标元素加组件，首先判断该行可编辑才加组件，否则跳过
        const node = e.target.attributes.ri.nodeValue
        const row = this.loadAllTableData[node]
        if (d.editEnable(fieldInfo.edit, row)) {
          this.recordEditorIconPosition(row, fieldInfo, e)
          switch (fieldInfo.type) {
            case 'text':
              d.openTextEditor(this, e, row, fieldInfo, tabTitle)
              break
            case 'number':
              d.openNumberEditor(this, e, row, fieldInfo, tabTitle)
              break
            case 'selector':
              d.openSelector(this, e, row, fieldInfo, tabTitle)
              break
            case 'date':
              d.openDatePicker(this, e, row, fieldInfo, tabTitle)
              break
            case 'click':
              d.clickEvent(this, e, node, row, fieldInfo, tabTitle)
              break
            case 'tree':
              d.openTreeSelector(this, e, row, fieldInfo, tabTitle)
              break
          }
        }
      }
    },
    /**
     * 记录单元格编辑位置，记录到行数据_c_meta_中
     * @param row
     * @param fieldInfo
     * @param e
     */
    recordEditorIconPosition(row, fieldInfo, e) {
      const tdRect = e.path.find(el => el.tagName === 'TD').getBoundingClientRect()
      row._c_meta_['edit_icon_' + fieldInfo.field] = {
        left: tdRect.left - this.$el.getBoundingClientRect().left - this.defaultEditorIconWidth,
        top: -this.defaultEditorIconWidth
      }
      if (fieldInfo.fixed !== 'left' && fieldInfo.fixed !== 'right') {
        row._c_meta_['edit_icon_' + fieldInfo.field].left += (this.scrollLeft - this.tableFixedMargin.leftNum)
      } else if (fieldInfo.fixed === 'right') {
        row._c_meta_['edit_icon_' + fieldInfo.field].left -= this.$refs.rightTableBody.getBoundingClientRect().left
      }
    },
    /**
     * 鼠标移动，是否可拖动列宽
     * @param e
     */
    thMouseMove(e) {
      if (e.target.nodeName !== 'TH' || e.target.attributes.cursor) {
        e.currentTarget.style.cursor = 'pointer'
        return
      }
      const attr = e.target.attributes
      const tabTitleArr = this.getTabTitleArr
      if (attr.pos && attr.fi && attr.pos.nodeValue && attr.fi.nodeValue) {
        const item = this.tabTitle.titles[attr.pos.nodeValue][attr.ri.nodeValue][attr.fi.nodeValue]
          const index = tabTitleArr.findIndex(el => el === item)
          if (index === tabTitleArr.length - 1) {
            return
          }
      }
      if (e.target.offsetWidth - e.offsetX <= 5) {
        e.currentTarget.style.cursor = 'ew-resize'
      } else {
        e.currentTarget.style.cursor = 'pointer'
      }
    },
    /**
     * 鼠标按下事件，判断是否可拖动列宽
     * @param e
     */
    thMouseDown(e) {
      this.synchroPreWith()
      const that = this
      const currentMouseX = e.screenX
      const offsetWidth = e.target.offsetWidth
      if (offsetWidth - e.offsetX <= 5) {
        const attr = e.target.attributes
        const tabTitleArr = this.getTabTitleArr
        if (attr.pos && attr.fi && attr.pos.nodeValue && attr.fi.nodeValue) {
          const item = that.tabTitle.titles[attr.pos.nodeValue][attr.ri.nodeValue][attr.fi.nodeValue]
          const index = tabTitleArr.findIndex(el => el === item)
          if (index === tabTitleArr.length - 1) {
            return
          }
          const nextItem = tabTitleArr[index + 1]
          if (!item._c_meta_.firstMoveWidth) {
            item._c_meta_.firstMoveWidth = item._c_meta_.width
          }
          if (!nextItem._c_meta_.firstMoveWidth) {
            nextItem._c_meta_.firstMoveWidth = nextItem._c_meta_.width
          }
          const totalWidth = item._c_meta_.firstMoveWidth + nextItem._c_meta_.firstMoveWidth
          document.onmousemove = function(e1) {
            that.noSelect = true
            const itemOldWidth = item._c_meta_.width
            const moveX = e1.screenX - currentMouseX
            const width = offsetWidth + moveX
            if (moveX < 0) {
              item._c_meta_.width = width > 10 ? width : 10
            } else {
              item._c_meta_.width = width
            }
            if (item._c_meta_.firstMoveWidth > item._c_meta_.width || (index === tabTitleArr.length - 2)) {
              nextItem._c_meta_.width = totalWidth - item._c_meta_.width
            }
            // 部分已编辑框位置重构
            Array.from(that.editedList).forEach(el => {
              if (el._c_meta_['edit_icon_' + nextItem.field]) {
                el._c_meta_['edit_icon_' + nextItem.field].left += (item._c_meta_.width - itemOldWidth)
              }
            })
            if (that.titleAttributeTable.length > 0) {
              that.fixAutoHeight()
            }
            // 列宽改变，单元格高度自适应 - 开启换行操作时
            if (that.isTableAttrOn('supportLineBreak')) {
              that.tableData.forEach(el => { if (el._c_meta_ && el._c_meta_.height) { delete el._c_meta_.height } })
              that.autoAdaptTRHeight()
            }
          }
          document.onmouseup = function(e2) {
            document.onmousemove = null
            document.onmouseup = null
            that.noSelect = false
          }
        }
      }
    },
    /**
     * 同步preTitle中间表格每一列的宽度，修复使用鼠标调节标题宽度bug
     */
    synchroPreWith() {
      this.tabTitle.columns.center.forEach(el => {
        el._c_meta_.width = el.width
      })
    },
    /**
     * 鼠标滚动
     * @param e
     * @param type
     */
    tableScroll(e, type) {
      this.scrollTop = e.srcElement.scrollTop
      if (type === 'center') {
        this.scrollLeft = e.srcElement.scrollLeft
      }
      this.editor.type = undefined
    },
    /**
     * 自动校正鼠标滚动条位置-用于键盘上、下移动编辑框
     * @param row
     */
    autoScroll(row) {
      const contentTableRect = this.$refs.contentTable.getBoundingClientRect()
      const rowRect = row.getBoundingClientRect()
      // 滚动条 上、下移动
      if (rowRect.bottom > contentTableRect.bottom) {
        this.scrollTop += (rowRect.bottom - contentTableRect.bottom + this.scrollBarWidthX)
      } else if (contentTableRect.top > rowRect.top) {
        this.scrollTop -= (contentTableRect.top - rowRect.top)
      }
    },
    /**
     * 递归执行func arrs为其children
     * @param arrs
     * @param func
     */
    forEachFieldItem(arrs, func) {
      const digui = function(array) {
        for (const item of array) {
          if (item.children) {
            digui(item.children)
          } else {
            func(item)
          }
        }
      }
      digui(arrs)
    },
    /**
     * 查询分页数据
     * @param pageNum
     * @param pageSize
     * @param callback
     */
    pageChange(pageNum, pageSize, callback) {
      const that = this
      if (!this.currentParam) {
        this.currentParam = {}
      }
      this.pageNum = pageNum
      this.pageSize = pageSize
      this.currentParam.pageNum = pageNum
      // 判断是否开启分页 - 不开启查所有
      this.currentParam.pageSize = this.pageable ? pageSize : null
      if (this.loadSpinning) {
        this.$refs.cspin.show()
      }
      this.$getAction(this.url, this.currentParam).then(res => {
          if (res.rows) {
            const list = res.rows
            if (that.loadBefore) {
              that.loadBefore(list)
            }
            // 扩展行初始化
            that.expansionData = []
            that.tableData = list
            this.total = parseInt(res.total)
            // 筛选条件重构 - 清理
            this.filterFieldMapClear()
          }
          if (callback) {
            callback(res)
          }
          // 数据加载完毕，渲染缓存的多选项
          if (that.rowKey) {
            that.setCheckBoxByPrimaryKey(Array.from(that.selectedMap.keys()).join(','), that.rowKey, true)
          }
          this.$emit('sizeChange', pageNum, pageSize)
        }).catch(err => {
          if (callback) {
            callback(null, err)
          }
        }).finally(() => {
          if (this.$refs.cspin) {
            this.$refs.cspin.hide()
          }
        })
    },
    /**
     * 获取当前页面选中数据
     * @returns {*[]}
     */
    getCurPageSelected() {
      const arr = []
      for (const item of this.loadAllTableData) {
        if (item.cChecked === true) {
          arr.push(item)
          if (this.selectedDeleteMap.get(item[this.rowKey])) {
            this.selectedDeleteMap.delete(item[this.rowKey])
          }
        } else if (item.hasOwnProperty('cChecked') && item.cChecked === false) {
          // 多选时被删除过的复选框
          this.selectedDeleteMap.set(item[this.rowKey], item)
        }
      }
      return arr
    },
    /**
     * 表格行颜色切换：全局共两种颜色用于区分，隔一行显示一种颜色：基数：#f9f9f9；偶数：#fff
     * @param index
     * @returns {string}
     */
    changeRowColor(index) {
      if (this.tableRowChangeColor) {
        if (index % 2 > 0) {
          return '#fff'
        } else {
          return '#f9f9f9'
        }
      } else {
      }
    },
    /**
     * 行数据拖动
     * @param e
     */
    nodeOnmousedown(e) {
      if (!this.tableMoveable || this.viewMode) {
        return
      }
      e.preventDefault()
      const _this = this
      _this.composedPath(e)
      const target = e.path.find(el => el.tagName === 'TD')
      this.currentRow = this.loadAllTableData[target.attributes.ri.nodeValue]
      const currentRow = this.currentRow
      if (this.expandable && currentRow._c_meta_.parentData) {
        return
      }
      const allTableData = [...this.loadAllTableData]
      const contentTable = this.$refs.contentTable
      const contentTop = contentTable.getBoundingClientRect().top
      const startNodeValue = parseInt(target.attributes.ri.nodeValue)
      // 记录鼠标在元素上的位置
      const posX = e.pageX - this.$el.offsetLeft - e.offsetX - 5
      // 迭代定位 - 找到最顶层元素 startNodeValue>endNodeValue
      const diedaiTop = (item) => {
        const parentData = item._c_meta_.parentData
        if (parentData) {
          return diedaiTop(parentData)
        }
        return item
      }
      // 迭代定位 - 找到最底层元素 startNodeValue<endNodeValue
      const diedaiBottom = (item) => {
        const childItems = this.expansionData.filter(el => el._c_meta_.parentData._c_meta_.key === item._c_meta_.key)
        if (childItems.length > 0) {
          return diedaiBottom(childItems[childItems.length - 1])
        }
        return item
      }
      contentTable.onmousemove = function(e1) {
        e1.preventDefault()
        _this.composedPath(e1)
        const domTD = e1.path.find(el => el.tagName === 'TD')
        if (domTD) {
          // 移动找到最顶层的数据 - 主要针对行展开数据，未展开不影响
          let endNodeValue = parseInt(domTD.attributes.ri.nodeValue)
          const item = startNodeValue > endNodeValue ? diedaiTop(allTableData[endNodeValue]) : diedaiBottom(allTableData[endNodeValue])
          endNodeValue = allTableData.findIndex(el => el._c_meta_.key === item._c_meta_.key)
          const dom = document.getElementById(item._c_meta_.key)
          let top = dom.getBoundingClientRect().top - contentTop
          if (startNodeValue < endNodeValue) {
            top += dom.offsetHeight
          }
          _this.setRemoveLine('block', top, dom.offsetLeft, 'block', e1.offsetY + e1.target.getBoundingClientRect().top - contentTop, e1.pageX - posX + 60)
        } else {
          _this.setRemoveLine('none', 0, 0, 'block', null, e1.pageX - posX)
        }
      }
      contentTable.onmouseup = function(e2) {
        e2.preventDefault()
        _this.composedPath(e2)
        const domTD = e2.path.find(el => el.tagName === 'TD')
        if (domTD) {
          let endNodeValue = parseInt(domTD.attributes.ri.nodeValue)
          const item = startNodeValue > endNodeValue ? diedaiTop(allTableData[endNodeValue]) : diedaiBottom(allTableData[endNodeValue])
          endNodeValue = allTableData.findIndex(el => el._c_meta_.key === item._c_meta_.key)
          if (diedaiTop(allTableData[startNodeValue])._c_meta_.key === diedaiTop(allTableData[endNodeValue])._c_meta_.key) {
            return
          }
          // 行拖动 - 添加动画 - 自动识别
          _this.autoSetTableDataTranslateY(startNodeValue, endNodeValue)
          setTimeout(() => {
            if (startNodeValue > endNodeValue) {
              _this.tableData = [
                ...allTableData.slice(0, endNodeValue),
                ...[currentRow],
                ...allTableData.slice(endNodeValue, startNodeValue),
                ...allTableData.slice(startNodeValue + 1, allTableData.length)
              ].filter(el => !el._c_meta_.parentData)
            } else if (endNodeValue > startNodeValue) {
              _this.tableData = [
                ...allTableData.slice(0, startNodeValue),
                ...allTableData.slice(startNodeValue + 1, endNodeValue + 1),
                ...[currentRow],
                ...allTableData.slice(endNodeValue + 1, allTableData.length)
              ].filter(el => !el._c_meta_.parentData)
            }
            _this.expansionData.forEach(el => { if (el._c_meta_.translateY) { el._c_meta_.top += el._c_meta_.translateY } })
            _this.loadAllTableData.forEach(el => { delete el._c_meta_.translateY })
            if (startNodeValue !== endNodeValue) { _this.currentRow = currentRow }
            _this.$emit('rowDrag')
            _this.$forceUpdate()
          }, 200)
        }
      }
      document.onmouseup = function(e3) {
        e3.preventDefault()
        contentTable.onmousemove = null
        contentTable.onmouseup = null
        _this.setRemoveLine('none', 0, 0, 'none', 0, 0)
      }
    },
    /**
     * 设置行数据拖动的辅助线
     * @param display
     * @param top
     * @param left
     * @param infoDisplay
     * @param infoTop
     * @param infoLeft
     */
    setRemoveLine(display, top, left, infoDisplay, infoTop, infoLeft) {
      if (display && this.removeLine.display !== display) {
        this.removeLine.display = display
      }
      if (top >= 0 && this.removeLine.top !== top) {
        this.removeLine.top = top
      }
      if (left && this.removeLine.left !== left) {
        this.removeLine.left = left
      }
      if (infoDisplay && this.removeLine.info.display !== infoDisplay) {
        this.removeLine.info.display = infoDisplay
      }
      if (infoTop && this.removeLine.info.top !== infoTop) {
        this.removeLine.info.top = infoTop
      }
      if (infoLeft && this.removeLine.info.left !== infoLeft) {
        this.removeLine.info.left = infoLeft
      }
    },
    /**
     * 自动设置表格数据 动画 - 行数据拖动时 动画距离自动设置
     * @param startNodeValue
     * @param endNodeValue
     */
    autoSetTableDataTranslateY(startNodeValue, endNodeValue) {
      let translateY = 0
      // 移动距离
      const num = endNodeValue - startNodeValue
      // 1.定位开始拖拽的元素 - 通过比对 找到其在loadAllTableData中到tableData中的下一个元素的距离（即：其展开了几个子节点）
      const startData = this.loadAllTableData[startNodeValue]
      let childItemsLength = 0
      let childItemsHeight = startData._c_meta_.height
      const diedai = (item) => {
        this.expansionData.forEach(el => {
          if (el._c_meta_.parentData._c_meta_.key === item._c_meta_.key) {
            diedai(el)
            childItemsHeight += el._c_meta_.height
            childItemsLength++
          }
        })
      }
      diedai(startData)
      // 计算移动的行高
      const rowHeight = (el) => {
        return (el._c_meta_ && el._c_meta_.height) ? el._c_meta_.height : this.getTableDefaultTDHeightBySize
      }
      // 所有数据初始化，重新计算
      this.loadAllTableData.forEach(el => { el._c_meta_.translateY = 0 })
      this.loadAllTableData.forEach((el, index) => {
        if (num > 0 && index > (startNodeValue + childItemsLength) && index <= endNodeValue) { // 向下移动
          el._c_meta_.translateY = -childItemsHeight
          translateY += rowHeight(el)
        } else if (num < 0 && index >= endNodeValue && index < startNodeValue) { // 向上移动
          el._c_meta_.translateY = childItemsHeight
          translateY += rowHeight(el)
        }
      })
      this.loadAllTableData.forEach((el, index) => {
        if (index >= startNodeValue && index <= (startNodeValue + childItemsLength)) {
          el._c_meta_.translateY = num > 0 ? translateY : -translateY
        }
      })
    },
    /**
     * 冒泡 兼容处理 - 兼容事件没有path属性 - 递归寻找其所有上层dom
     * @param e
     * @returns {[]}
     */
    composedPath (e) {
      // 存在则直接return
      if (e.path) { return e.path }
      // 不存在则遍历target节点
      let target = e.target
      e.path = []
      while (target.parentNode !== null) {
        e.path.push(target)
        target = target.parentNode
      }
      // 最后补上document和window
      e.path.push(document, window)
      return e.path
    },
    /**
     * 分页发生变化事件
     * @param pageNum
     * @param pageSize
     */
    sizeChange(pageNum, pageSize) {
      this.pageNum = 1
      this.pageChange(1, this.pageSize)
    },
    /**
     * 标题字段是否显示
     * @param e
     */
    hideColumnChange(e) {
      const target = this.visibleTabTitle.arr.find(el => el.title === e.target.value)
      if (target) {
        this.$set(target, 'hidden', !e.target.checked)
      }
    },
    /**
     * Excel导出确认 - 提前获取到已筛选的所有数据（高级筛选）
     * @param allData
     */
    exportExcel(allData) {
      const that = this
      this.$confirm('是否下载当前表格可加载的所有数据？').then(() => {
        that.leadingOutExcel(allData)
        that.$message.success('Excel导出中...')
      })
    },
    /**
     * Excel选中数据导出确认 - 提前获取到已筛选的所有数据（高级筛选）
     * @param allData
     */
    exportExcelSelected(allData) {
      const that = this
      this.$confirm('是否下载当前表格选中的数据？').then(() => {
        that.leadingOutExcel(allData)
        that.$message.success('Excel导出中...')
      })
    },
    /**
     * excel导出操作
     * @param allData
     */
    leadingOutExcel(allData) {
      // 获取所有标题信息=》二维数组
      let titleList = this.loadAllTitles()
      // 获取查询所有数据
      let tableData = this.pretreatmentData(allData)
      if (this.excelInfo && this.excelInfo.pretreatment) {
        // 是否开启手动预处理=》excelInfo.pretreatment
        // 若需要手动处理导出数据，会向上抛出pretreatment，参数为标题和数据
        this.$emit('pretreatment', titleList, tableData, (title, value) => {
          if (title) {
            titleList = title
          }
          tableData = value
          this.executeDownLoad(titleList, tableData)
        })
      } else {
        this.executeDownLoad(titleList, tableData)
      }
    },
    /**
     * 导出-加载所有标题信息，二维数组
     * @returns {*[]}
     */
    loadAllTitles() {
      // 筛选表头信息
      const titleList = []
      const titleLastList = [
        {
          key: 'index',
          value: '序号',
          rowspan: this.tabTitle.titles.left.length,
          colspan: 1,
          copy_num: this.tabTitle.titles.left.length - 1
        }
      ]
      // 获取第一行标题所有对象（对象完整，存在children，便于迭代找到最后一行标题所有字段，再从下往上复制对象，并替换其父级字段，最后筛选合并行、列（打标记））
      const preTitles = [
        ...this.tabTitle.titles.left[0],
        ...this.tabTitle.titles.center[0],
        ...this.tabTitle.titles.right[0]
      ]
      d.getLastTitleRow(d.copyObj(preTitles), titleLastList)
      titleList[this.tabTitle.titles.left.length - 1] = { row: titleLastList }
      for (let row = this.tabTitle.titles.left.length - 1; row > 0; row--) {
        const temp = []
        for (const obj of d.copyObj(titleList[row].row)) {
          if (obj.copy_num && obj.copy_num > 0) {
            obj.copy_num--
            temp.push(obj)
          } else if (obj.parent) {
            temp.push(d.copyObj(obj.parent))
          }
        }
        titleList[row - 1] = { row: temp }
      }
      d.buildTitleList(titleList)
      return titleList
    },
    /**
     * 导出-预处理已获取数据
     * @param allData
     * @returns {*[]}
     */
    pretreatmentData(allData) {
      const tableData = []
      // 获取表头信息，用于format字段值
      const columnData = [
        ...this.tabTitle.columns.left,
        ...this.tabTitle.columns.center,
        ...this.tabTitle.columns.right
      ]
      allData.forEach((el, index) => {
        const array = [{ key: 'index', value: index + 1 }]
        for (const key of Object.keys(el)) {
          const fieldItem = columnData.find(el => el.field === key)
          if (fieldItem) {
            array.push({ key: key, value: this.formatExcelCellValue(fieldItem, el) })
          } else if (el[key] && typeof el[key] === 'string') {
            array.push({ key: key, value: el[key] })
          } else if (!el[key]) {
            array.push({ key: key, value: null })
          }
        }
        tableData.push({ row: array })
      })
      return tableData
    },
    /**
     * 数据格式化
     * @param fieldItem
     * @param dataItem
     * @returns {*}
     */
    formatExcelCellValue(fieldItem, dataItem) {
      let value = ''
      if (fieldItem.type === 'dynamic') {
        // &&fieldItem.dynamic&&typeof(dynamic)=='function'
        const tempFieldInfo = fieldItem.dynamic(this, dataItem)
        if (tempFieldInfo && tempFieldInfo.formater) {
          value = tempFieldInfo.formater(dataItem[fieldItem.field], dataItem)
        } else {
          value = dataItem[fieldItem.field]
        }
      } else if (fieldItem.formater && typeof fieldItem.formater === 'function') {
        value = fieldItem.formater(dataItem[fieldItem.field], dataItem)
      } else {
        value = dataItem[fieldItem.field]
      }
      if (fieldItem.type === 'switch') {
        if (fieldItem.formater) {
          const v = fieldItem.formater(dataItem[fieldItem.field], dataItem)
          value = v ? '是' : '否'
        } else {
          const on = fieldItem.on ? fieldItem.on : true
          value = value === on ? '是' : '否'
        }
      } else if (fieldItem.type === 'checkBox') {
        if (fieldItem.formater) {
          const v = fieldItem.formater(dataItem[fieldItem.field], dataItem)
          value = v ? '是' : '否'
        } else {
          const on = fieldItem.on ? fieldItem.on : true
          value = value === on ? '是' : '否'
        }
      }
      return value
    },
    /**
     * 执行导出操作
     * @param titleList
     * @param tableData
     */
    executeDownLoad(titleList, tableData) {
      const params = [
        {
          sheetName: this.excelInfo && this.excelInfo.sheetName ? this.excelInfo.sheetName : '数据',
          titleList: titleList,
          valueList: tableData
        }
      ]
      d.downloadExcel(this.excelInfo && this.excelInfo.fileName ? this.excelInfo.fileName : '数据清单', this.excelUrl, JSON.stringify(params)
      )
    },
    /**
     * 设置表格属性全局缓存 - 通过用户存取
     * @param checked
     * @param e
     * @param key
     */
    setTableAttr(checked, e, key) {
      this.$store.dispatch('table/setTableAttr', { key: key, val: checked })
      if (key === 'supportLineBreak') {
        this.tableData.forEach(el => { if (el._c_meta_ && el._c_meta_.height) { delete el._c_meta_.height } })
        this.autoAdaptTRHeight()
      }
    },
    /**
     * 表格列编辑后组件内容刷出到原数据行时触发，即表格数据被修改时触发
     */
    flushEditState() {
      if (this.editor.row && this.editor.fieldInfo) {
        if (this.editor.row[this.editor.fieldInfo.field] !== this.editor.origValue) {
          this.$emit('change', this.editor.row, this.editor.fieldInfo.field)
          d.setOutValue(this, this.editor.row, this.editor.fieldInfo, this.editor.tempVal)
          this.$forceUpdate()
        }
      }
    },
    /**
     * 表格列编辑输入字符时触发,将编辑行和编辑的字段传入
     */
    tdChange() {
      this.$emit('input', { fieldName: this.editor.fieldName, row: this.editor.row })
    },
    /**
     * 获取全局唯一key值 - 通过当前时间戳 和 随机数生成 - 生成后在onlyKeySet集合中查找，找到代表已存在，重新生成16位
     * @returns {*|string}
     */
    getOnlyKey() {
      const random = Math.floor(Math.random() * 10) + ''
      const key = new Date().getTime() + random + random
      if (this.onlyKeySet.has(key)) {
        return this.getOnlyKey()
      } else {
        this.onlyKeySet.add(key)
        return key
      }
    },
  },
  watch: {
    /**
     * 表格size变化 实时监控
     */
    size() {
      this.tableData.forEach(el => { if (el._c_meta_ && el._c_meta_.height) { delete el._c_meta_.height } })
      this.autoAdaptTRHeight()
    },
    'editor.type': function(value) {
      if (value === undefined) {
        this.flushEditState()
      }
    },
    scrollTop: function(val) {
      this.$refs.leftTableBody.scrollTop = val
      this.$refs.rightTableBody.scrollTop = val
      this.$refs.centerTableBody.scrollTop = val
    },
    scrollLeft: function(val) {
      this.$refs.centerTableHead.scrollLeft = val
      // 动态修改自定义展开行随滚动条移动位置
      this.loadAllTableData.forEach(el => {
        if (el._c_meta_.parentData && this.$refs['expand_row_' + el._c_meta_.key]) {
          this.$refs['expand_row_' + el._c_meta_.key][0].style.left = this.$refs.centerTableHead.scrollLeft + 'px'
        }
      })
    },
    /**
     * 表格标题发生变化
     * @param val
     */
    tabTitle(val) {
      const cr = this.tableBoundingClientRect
      let autoHeight = cr.height - this.defaultTableTitleHeight * this.tabTitle.titles.left.length
      if (this.$refs.cparams && this.$refs.cparams.$el.nodeName === 'DIV') {
        autoHeight -= this.$refs.cparams.$el.getBoundingClientRect().height
      }
      if (this.pageable) {
        autoHeight -= (this.defaultPageHeight + 5)
      }
      this.tableAutoHeight = autoHeight
    },
    /**
     * 标题数据预处理
     */
    tableTitle: {
      immediate: true,
      deep: true,
      handler(val, oldVal) {
        const that = this
        const tableTitle = d.copyObj(val)
        this.forEachFieldItem(tableTitle, item => {
          if (!item.type) {
            item.type = 'text'
          }
          if (item.type === 'selector') {
            preHandler.selector(that, item)
          } else if (item.type === 'tree') {
            preHandler.tree(that, item)
          } else if (item.type === 'number') {
            preHandler.number(that, item)
          } else if (item.type === 'click') {
            preHandler.click(that, item)
          }
        })
        this.preTableTitle = tableTitle
      }
    },
    /**
     * 获取数据监听 - 实时赋值（属性）_c_meta_
     */
    tableData() {
      this.tableData.forEach((el, index) => {
        el._c_meta_ = el._c_meta_ ? el._c_meta_ : {}
        el._c_meta_.key = el._c_meta_.key ? el._c_meta_.key : this.getOnlyKey()
        el._c_meta_.number = (index < 9) ? ('0' + (index + 1)) : (index + 1)
        el._c_meta_.expandIcon = (this.expandable && this.expandCustom) || (this.expandable && !this.expandCustom && !!el.children)
        el._c_meta_.level = 1
        el._c_meta_.paddingLeft = 0
      })
      this.autoAdaptTRHeight()
    }
  }
}
</script>

<style lang="less" scoped>
::v-deep .ant-pagination-prev,::v-deep .ant-pagination-item,::v-deep .ant-pagination-next{
  min-width: 28px;
  line-height:28px;
  width:28px;
  height:28px;
}
::v-deep .ant-select-selection--single,::v-deep .ant-pagination-options-quick-jumper,::v-deep .ant-pagination-options-quick-jumper input{
  height:28px;
  line-height:28px;
}
::v-deep .ant-pagination-item-active{
  background: @primary-color;
  border:none;
}
::v-deep .ant-pagination-item-active a{
  font-weight: 500;
  color: #FFFFFF;
  -webkit-background-clip: text;
}
::v-deep .ant-pagination-item{
  >a{
    width:28px;
  }
}

::v-deep .ant-switch-checked{
  background-color: @primary-color;
}
::v-deep .ant-pagination-total-text{
  line-height:32px;
}
::v-deep .ant-pagination-options-size-changer.ant-select{
  margin-right:0px;
}
::v-deep .ant-pagination-options-quick-jumper{
  margin-left:8px;
}
::v-deep .ant-pagination-options{
  height: 28px;
}
</style>
